MythTV master
synaesthesia.cpp
Go to the documentation of this file.
1// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
2//
3// Use, modification and distribution is allowed without limitation,
4// warranty, or liability of any kind.
5//
6// modified 12-2004 by Kyle Schlansker to add 64 bit support
7//
8
9// C++
10#include <algorithm>
11#include <cinttypes>
12#include <cmath>
13#include <cstdint>
14#include <cstdlib>
15#include <iostream>
16
17// Qt
18#include <QCoreApplication>
19#include <QPainter>
20#include <QImage>
21#include <numbers>
22
23// MythTV
24#include <libmythbase/compat.h>
26
27// MythMusic
28#include "mainvisual.h"
29#include "synaesthesia.h"
30
32{
33 m_fps = 29;
34
35 coreInit(); // init cosTable, negSinTable, bitReverse
36 setStarSize(m_starSize); // init scaleDown, maxStarRadius
37 setupPalette(); // init palette
38}
39
41{
42 delete m_outputImage;
43}
44
45//static constexpr uint8_t sBOUND(int x)
46// { return std::clamp(x, 0, 255); };
47//static constexpr uint8_t sPEAKIFY(int x)
48// {return sBOUND(x - x*(255-x)/255/2); };
49
51{
52 double fgRed = m_fgRedSlider;
53 double fgGreen = m_fgGreenSlider;
54 double fgBlue = 1.0 - std::max(m_fgRedSlider,m_fgGreenSlider);
55 //double scale = std::max({fgRed,fgGreen,fgBlue});
56 double scale = (fgRed + fgGreen + fgBlue) / 2.0;
57 fgRed /= scale;
58 fgGreen /= scale;
59 fgBlue /= scale;
60
61 double bgRed = m_bgRedSlider;
62 double bgGreen = m_bgGreenSlider;
63 double bgBlue = 1.0 - std::max(m_bgRedSlider,m_bgGreenSlider);
64 //scale = std::max({bgRed,bgGreen,bgBlue});
65 scale = (bgRed + bgGreen + bgBlue) / 2.0;
66 bgRed /= scale;
67 bgGreen /= scale;
68 bgBlue /= scale;
69
70 for (int i = 0; i < 256; i++) {
71 int f = i & 15;
72 int b = i / 16;
73 //m_palette[i * 3 + 0] = sPEAKIFY(b*bgRed*16+f*fgRed*16);
74 //m_palette[i * 3 + 1] = sPEAKIFY(b*bgGreen*16+f*fgGreen*16);
75 //m_palette[i * 3 + 2] = sPEAKIFY(b*bgBlue*16+f*fgBlue*16);
76
77 double red = (b * bgRed * 16) + (f * fgRed * 16);
78 double green = (b * bgGreen * 16) + (f * fgGreen * 16);
79 double blue = (b * bgBlue * 16) + (f * fgBlue * 16);
80
81 double excess = 0.0;
82 for (int j = 0; j < 5; j++)
83 {
84 red += excess / 3;
85 green += excess / 3;
86 blue += excess / 3;
87 excess = 0.0;
88
89
90 if (red > 255) { excess += red - 255; red = 255; }
91 if (green > 255) { excess += green - 255; green = 255; }
92 if (blue > 255) { excess += blue - 255; blue = 255; }
93 }
94
95 double scale2 = (0.5 + (red + green + blue) / 768.0) / 1.5;
96 red *= scale2;
97 green *= scale2;
98 blue *= scale2;
99
100 m_palette[(i * 3) + 0] = std::clamp(int(red), 0, 255);
101 m_palette[(i * 3) + 1] = std::clamp(int(green), 0, 255);
102 m_palette[(i * 3) + 2] = std::clamp(int(blue), 0, 255);
103 }
104}
105
106void Synaesthesia::resize(const QSize &newsize)
107{
108 m_size = newsize;
109
110 m_size.setHeight(m_size.height() / 2);
111 m_size.setWidth((m_size.width() / 4) * 4);
112 m_outputBmp.size(m_size.width(), m_size.height());
113 m_lastOutputBmp.size(m_size.width(), m_size.height());
114 m_lastLastOutputBmp.size(m_size.width(), m_size.height());
115 m_outWidth = m_size.width();
116 m_outHeight = m_size.height();
117
118 delete m_outputImage;
119
120 m_size.setHeight(m_size.height() * 2);
121 m_outputImage = new QImage(m_size, QImage::Format_Indexed8);
122
123 if (!m_outputImage)
124 {
125 LOG(VB_GENERAL, LOG_ERR,
126 "outputImage in Synaesthesia::resize() is NULL");
127 return;
128 }
129
130 for (size_t i = 0; i < 256; i++)
131 m_outputImage->setColor(i, qRgba(m_palette[i * 3], m_palette[(i * 3) + 1],
132 m_palette[(i * 3) + 2], 255));
133
134#if 0
135 surface = SDL_SetVideoMode(size.width(), size.height(), 8, 0);
136
137 if (!surface)
138 {
139 LOG(VB_GENERAL, LOG_ERR, "Couldn't get SDL surface");
140 return;
141 }
142
143 SDL_Color sdlPalette[256];
144
145 for (int i = 0; i < 256; i++)
146 {
147 sdlPalette[i].r = m_palette[i * 3];
148 sdlPalette[i].g = m_palette[i * 3 + 1];
149 sdlPalette[i].b = m_palette[i * 3 + 2];
150 }
151
152 SDL_SetColors(surface, sdlPalette, 0, 256);
153#endif
154}
155
157{
158 int sum = 0;
159 for (size_t j = 0; j < LogSize; j++)
160 {
161 sum = (i & 1) + (sum * 2);
162 i >>= 1;
163 }
164
165 return sum;
166}
167
168void Synaesthesia::fft(double *x, double *y)
169{
170 size_t n2 = NumSamples;
171 for (size_t twoToTheK = 1; twoToTheK < NumSamples; twoToTheK *= 2)
172 {
173 size_t n1 = n2;
174 n2 /= 2;
175 for (size_t j = 0; j < n2; j++)
176 {
177 double c = m_cosTable[(j * twoToTheK) & (NumSamples - 1)];
178 double s = m_negSinTable[(j * twoToTheK) & (NumSamples - 1)];
179 for (size_t i = j; i < NumSamples; i += n1)
180 {
181 size_t l = i + n2;
182 double xt = x[i] - x[l];
183 x[i] = (x[i] + x[l]);
184 double yt = y[i] - y[l];
185 y[i] = (y[i] + y[l]);
186 x[l] = (xt * c) - (yt * s);
187 y[l] = (xt * s) + (yt * c);
188 }
189 }
190 }
191}
192
194{
195 double fadeModeFudge { 0.78 };
196 if (m_fadeMode == Wave)
197 fadeModeFudge = 0.4;
198 else if (m_fadeMode == Flame)
199 fadeModeFudge = 0.6;
200
201 int factor = 0;
202 if (lsize > 0.0)
203 factor = int(exp(log(fadeModeFudge) / (lsize * 8.0)) * 255);
204 factor = std::min(factor, 255);
205
206 for (int i = 0; i < 256; i++)
207 m_scaleDown[i] = i * factor>>8;
208
209 m_maxStarRadius = 1;
210 for (int i = 255; i; i = m_scaleDown[i])
212}
213
215{
216 for (size_t i = 0; i < NumSamples; i++)
217 {
218 m_negSinTable[i] = -sin(std::numbers::pi * 2.0 / NumSamples * i);
219 m_cosTable[i] = cos(std::numbers::pi * 2.0 / NumSamples * i);
221 }
222}
223
224#define output ((unsigned char*)m_outputBmp.data)
225#define lastOutput ((unsigned char*)m_lastOutputBmp.data)
226#define lastLastOutput ((unsigned char*)m_lastLastOutputBmp.data)
227
228void Synaesthesia::addPixel(int x, int y, int br1, int br2) const
229{
230 if (x < 0 || x > m_outWidth || y < 0 || y >= m_outHeight)
231 return;
232
233 unsigned char *p = output + (static_cast<ptrdiff_t>(x) * 2) +
234 (static_cast<ptrdiff_t>(y) * m_outWidth * 2);
235 if (p[0] + br1 < 255)
236 p[0] += br1;
237 else
238 p[0] = 255;
239 if (p[1] + br2 < 255)
240 p[1] += br2;
241 else
242 p[1] = 255;
243}
244
245void Synaesthesia::addPixelFast(unsigned char *p, int br1, int br2)
246{
247 if (p[0] + br1 < 255)
248 p[0] += br1;
249 else
250 p[0] = 255;
251 if (p[1] + br2 < 255)
252 p[1] += br2;
253 else
254 p[1] = 255;
255}
256
257unsigned char Synaesthesia::getPixel(int x, int y, int where) const
258{
259 if (x < 0 || y < 0 || x >= m_outWidth || y >= m_outHeight)
260 return 0;
261
262 return lastOutput[where];
263}
264
266{
267 auto *ptr = (uint32_t *)output;
268 for (int i = static_cast<ptrdiff_t>(m_outWidth) * m_outHeight * 2 / sizeof(uint32_t);
269 i > 0; i--)
270 {
271 uint32_t x = *ptr;
272 if (x)
273 {
274 *(ptr++) = x - ((x & (uintptr_t)0xf0f0f0f0) >> 4) -
275 ((x & (uintptr_t)0xe0e0e0e0) >> 5);
276 }
277 else
278 {
279 ptr++;
280 }
281 }
282}
283
284void Synaesthesia::fadePixelWave(int x, int y, int where, int step)
285{
286 short j = short((int(getPixel(x - 1, y, where - 2)) +
287 int(getPixel(x + 1, y, where + 2)) +
288 int(getPixel(x, y - 1, where - step)) +
289 int(getPixel(x, y + 1, where + step))) >> 2) +
290 lastOutput[where];
291
292 if (!j)
293 {
294 output[where] = 0;
295 return;
296 }
297 j = j - lastLastOutput[where] - 1;
298 if (j < 0)
299 output[where] = 0;
300 else if (j & (255 * 256))
301 output[where] = 255;
302 else
303 output[where] = j;
304}
305
307{
308 unsigned short *t = m_lastLastOutputBmp.data;
312
313 int step = m_outWidth*2;
314 for (int x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
315 x < m_outWidth; x++, i += 2, j += 2)
316 {
317 fadePixelWave(x, 0, i, step);
318 fadePixelWave(x, 0, i + 1, step);
319 fadePixelWave(x, m_outHeight - 1, j, step);
320 fadePixelWave(x, m_outHeight - 1, j + 1, step);
321 }
322
323 for (int y = 1, i = m_outWidth * 2, j = (m_outWidth * 4) - 2;
324 y < m_outHeight; y++, i += step, j += step)
325 {
326 fadePixelWave(0, y, i, step);
327 fadePixelWave(0, y, i + 1, step);
328 fadePixelWave(m_outWidth - 1, y, j, step);
329 fadePixelWave(m_outWidth - 1, y, j + 1, step);
330 }
331
332 for (int y = 1, start = (m_outWidth * 2) + 2, end = (m_outWidth * 4) - 2;
333 y < m_outHeight - 1; y++, start += step, end += step)
334 {
335 for (int i2 = start; i2 < end; i2++)
336 {
337 short j2 = short((int(lastOutput[i2 - 2]) +
338 int(lastOutput[i2 + 2]) +
339 int(lastOutput[i2 - step]) +
340 int(lastOutput[i2 + step])) >> 2) +
341 lastOutput[i2];
342 if (!j2)
343 {
344 output[i2] = 0;
345 }
346 else
347 {
348 j2 = j2 - lastLastOutput[i2] - 1;
349 if (j2 < 0)
350 output[i2] = 0;
351 else if (j2 & (255*256))
352 output[i2] = 255;
353 else
354 output[i2] = j2;
355 }
356 }
357 }
358}
359
360void Synaesthesia::fadePixelHeat(int x, int y, int where, int step)
361{
362 short j = short((int(getPixel(x - 1, y, where - 2)) +
363 int(getPixel(x + 1, y, where + 2)) +
364 int(getPixel(x, y - 1, where - step)) +
365 int(getPixel(x, y + 1, where + step))) >> 2) +
366 lastOutput[where];
367 if (!j)
368 {
369 output[where] = 0;
370 return;
371 }
372 j = j -lastLastOutput[where] - 1;
373 if (j < 0)
374 output[where] = 0;
375 else if (j & (255 * 256))
376 output[where] = 255;
377 else
378 output[where] = j;
379}
380
382{
383 unsigned short *t = m_lastLastOutputBmp.data;
387
388 int step = m_outWidth * 2;
389 for (int x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
390 x < m_outWidth; x++, i += 2, j += 2)
391 {
392 fadePixelHeat(x, 0, i, step);
393 fadePixelHeat(x, 0, i + 1, step);
394 fadePixelHeat(x, m_outHeight - 1, j, step);
395 fadePixelHeat(x, m_outHeight - 1, j + 1, step);
396 }
397
398 for(int y = 1, i = m_outWidth * 2, j = (m_outWidth * 4) - 2; y < m_outHeight;
399 y++, i += step, j += step)
400 {
401 fadePixelHeat(0, y, i, step);
402 fadePixelHeat(0, y, i + 1, step);
403 fadePixelHeat(m_outWidth - 1, y, j, step);
404 fadePixelHeat(m_outWidth - 1, y, j + 1, step);
405 }
406
407 for(int y = 1, start = (m_outWidth * 2) + 2, end = (m_outWidth * 4) - 2;
408 y < m_outHeight - 1; y++, start += step, end += step)
409 {
410 for (int i2 = start; i2 < end; i2++)
411 {
412 short j2 = short((int(lastOutput[i2 - 2]) +
413 int(lastOutput[i2 + 2]) +
414 int(lastOutput[i2 - step]) +
415 int(lastOutput[i2 + step])) >> 2) +
416 lastOutput[i2];
417 if (!j2)
418 output[i2] = 0;
419 else
420 {
421 j2 = j2 - lastLastOutput[i2] +
422 ((lastLastOutput[i2] - lastOutput[i2]) >> 2) - 1;
423 if (j2 < 0)
424 output[i2] = 0;
425 else if (j2 & (255*256))
426 output[i2] = 255;
427 else
428 output[i2] = j2;
429 }
430 };
431 }
432}
433
435{
436 switch(m_fadeMode)
437 {
438 case Stars: fadeFade(); break;
439 case Flame: fadeHeat(); break;
440 case Wave: fadeWave(); break;
441 default: break;
442 }
443}
444
446{
447 fade();
448
449 if (!node)
450 return false;
451
452 samp_dbl_array x {};
453 samp_dbl_array y {};
454 samp_dbl_array a {};
455 samp_dbl_array b {};
456 samp_int_array clarity {};
457
458 int brightFactor = int(Brightness * m_brightnessTwiddler / (m_starSize + 0.01));
459
460 size_t numSamps = NumSamples;
461 if (node->m_length < NumSamples)
462 numSamps = node->m_length;
463
464 for (size_t i = 0; i < numSamps; i++)
465 {
466 x[i] = node->m_left[i];
467 if (node->m_right)
468 y[i] = node->m_right[i];
469 else
470 y[i] = x[i];
471 }
472
473 fft(x.data(), y.data());
474
475 double energy = 0.0;
476
477 for (size_t i = 0 + 1; i < NumSamples / 2; i++)
478 {
479 double x1 = x[m_bitReverse[i]];
480 double y1 = y[m_bitReverse[i]];
481 double x2 = x[m_bitReverse[NumSamples - i]];
482 double y2 = y[m_bitReverse[NumSamples - i]];
483 double aa = ((x1 + x2) * (x1 + x2)) + ((y1 - y2) * (y1 - y2));
484 double bb = ((x1 - x2) * (x1 - x2)) + ((y2 + y2) * (y1 + y2));
485 a[i] = sqrt(aa);
486 b[i] = sqrt(bb);
487 if (aa + bb != 0.0)
488 {
489 clarity[i] = (int)(((x1 + x2) * (x1 - x2) + (y1 + y2) * (y1 - y2)) /
490 (aa + bb) * 256);
491 }
492 else
493 {
494 clarity[i] = 0;
495 }
496
497 energy += (aa + bb) * i * i;
498 }
499
500 energy = sqrt(energy / NumSamples) / 65536.0;
501
502 //int heightFactor = NumSamples / 2 / outHeight + 1;
503 //int actualHeight = NumSamples / 2 / heightFactor;
504 //int heightAdd = (outHeight + actualHeight) >> 1;
505
506 double brightFactor2 = (brightFactor / 65536.0 / NumSamples) *
507 sqrt(m_outHeight * m_outWidth / (320.0 * 200.0));
508
509 m_energyAvg = (m_energyAvg * 0.95) + (energy * 0.05);
510 if (m_energyAvg > 0.0)
511 brightFactor2 *= 80.0 / (m_energyAvg + 5.0);
512
513 for (size_t i = 1; i < NumSamples / 2; i++)
514 {
515 if (a[i] > 0 || b[i] > 0)
516 {
517 int h = (int)(b[i] * m_outWidth / (a[i] + b[i]));
518 int br = (int)((a[i] + b[i]) * i * brightFactor2);
519 int br1 = br * (clarity[i] + 128) >> 8;
520 int br2 = br * (128 - clarity[i]) >> 8;
521 if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255;
522 if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255;
523
524 int px = h;
525 int py = m_outHeight - (i * m_outHeight / (NumSamples / 2));
526
528 {
529 addPixel(px, py, br1, br2);
530 br1 = m_scaleDown[br1];
531 br2 = m_scaleDown[br2];
532
533 for(int j = 1; br1 > 0 || br2 > 0;
534 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
535 {
536 for (int k = 0; k < j; k++)
537 {
538 addPixel(px - j + k,py - k, br1, br2);
539 addPixel(px + k, py - j + k, br1, br2);
540 addPixel(px + j - k, py + k, br1, br2);
541 addPixel(px - k, py + j - k, br1, br2);
542 }
543 }
544 }
545 else
546 {
547 if (px < m_maxStarRadius || py < m_maxStarRadius ||
548 px > m_outWidth - m_maxStarRadius ||
550 {
551 addPixel(px, py, br1, br2);
552 for(int j = 1; br1 > 0 || br2 > 0;
553 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
554 {
555 addPixel(px + j, py, br1, br2);
556 addPixel(px, py + j, br1, br2);
557 addPixel(px - j, py, br1, br2);
558 addPixel(px, py - j, br1, br2);
559 }
560 }
561 else
562 {
563 unsigned char *p = output + (static_cast<ptrdiff_t>(px) * 2) +
564 (static_cast<ptrdiff_t>(py) * m_outWidth * 2);
565 unsigned char *p1 = p;
566 unsigned char *p2 = p;
567 unsigned char *p3 = p;
568 unsigned char *p4 = p;
569 addPixelFast(p, br1, br2);
570 for (; br1 > 0 || br2 > 0;
571 br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
572 {
573 p1 += 2;
574 addPixelFast(p1, br1, br2);
575 p2 -= 2;
576 addPixelFast(p2, br1, br2);
577 p3 += static_cast<ptrdiff_t>(m_outWidth) * 2;
578 addPixelFast(p3, br1, br2);
579 p4 -= static_cast<ptrdiff_t>(m_outWidth) * 2;
580 addPixelFast(p4, br1, br2);
581 }
582 }
583 }
584 }
585 }
586
587 return false;
588}
589
590bool Synaesthesia::draw(QPainter *p, [[maybe_unused]] const QColor &back)
591{
592 if (!m_outputImage)
593 return true;
594
595 auto *ptrOutput = (uint32_t *)output;
596
597 for (int j = 0; j < m_outHeight * 2; j += 2)
598 {
599 auto *ptrTop = (uint32_t *)(m_outputImage->scanLine(j));
600 auto *ptrBot = (uint32_t *)(m_outputImage->scanLine(j+1));
601
602 for (int i = m_outWidth / 4; i > 0; i--)
603 {
604 unsigned int const r1 = *(ptrOutput++);
605 unsigned int const r2 = *(ptrOutput++);
606
607 unsigned int const v = ((r1 & 0x000000f0UL) >> 4) |
608 ((r1 & 0x0000f000UL) >> 8) |
609 ((r1 & 0x00f00000UL) >> 12) |
610 ((r1 & 0xf0000000UL) >> 16);
611
612 *(ptrTop++) = v | (((r2 & 0x000000f0UL) << 12) |
613 ((r2 & 0x0000f000UL) << 8) |
614 ((r2 & 0x00f00000UL) << 4) |
615 ((r2 & 0xf0000000UL)));
616
617 *(ptrBot++) = v | (((r2 & 0x000000f0UL) << 12) |
618 ((r2 & 0x0000f000UL) << 8) |
619 ((r2 & 0x00f00000UL) << 4) |
620 ((r2 & 0xf0000000UL)));
621 }
622 }
623
624 p->drawImage(0, 0, *m_outputImage);
625
626 return true;
627}
628
629static class SynaesthesiaFactory : public VisFactory
630{
631 public:
632 const QString &name(void) const override // VisFactory
633 {
634 static QString s_name = QCoreApplication::translate("Visualizers",
635 "Synaesthesia");
636 return s_name;
637 }
638
639 uint plugins(QStringList *list) const override // VisFactory
640 {
641 *list << name();
642 return 1;
643 }
644
645 VisualBase *create([[maybe_unused]] MainVisual *parent,
646 [[maybe_unused]] const QString &pluginName) const override // VisFactory
647 {
648 return new Synaesthesia();
649 }
void size(int w, int h)
Definition: polygon.h:18
Pixel * data
Definition: polygon.h:13
VisualBase * create(MainVisual *parent, const QString &pluginName) const override
const QString & name(void) const override
uint plugins(QStringList *list) const override
void fadeFade(void) const
Synaesthesia(void)
Bitmap< unsigned short > m_outputBmp
Definition: synaesthesia.h:70
void resize(const QSize &size) override
static void addPixelFast(unsigned char *p, int br1, int br2)
~Synaesthesia() override
static int bitReverser(int i)
double m_bgGreenSlider
Definition: synaesthesia.h:79
samp_int_array m_bitReverse
Definition: synaesthesia.h:59
void fadePixelHeat(int x, int y, int where, int step)
void fade(void)
void fft(double *x, double *y)
bool process(VisualNode *node) override
void setStarSize(double lsize)
void coreInit(void)
std::array< uint8_t, 768 > m_palette
Definition: synaesthesia.h:75
void fadeWave(void)
double m_fgRedSlider
Definition: synaesthesia.h:76
unsigned char getPixel(int x, int y, int where) const
std::array< int, 256 > m_scaleDown
Definition: synaesthesia.h:60
samp_dbl_array m_cosTable
Definition: synaesthesia.h:57
QImage * m_outputImage
Definition: synaesthesia.h:73
void addPixel(int x, int y, int br1, int br2) const
void setupPalette(void)
bool m_pointsAreDiamonds
Definition: synaesthesia.h:63
int m_maxStarRadius
Definition: synaesthesia.h:61
bool draw(QPainter *p, const QColor &back) override
double m_starSize
Definition: synaesthesia.h:65
void fadeHeat(void)
double m_fgGreenSlider
Definition: synaesthesia.h:77
Bitmap< unsigned short > m_lastLastOutputBmp
Definition: synaesthesia.h:72
double m_energyAvg
Definition: synaesthesia.h:81
Bitmap< unsigned short > m_lastOutputBmp
Definition: synaesthesia.h:71
double m_brightnessTwiddler
Definition: synaesthesia.h:64
samp_dbl_array m_negSinTable
Definition: synaesthesia.h:58
double m_bgRedSlider
Definition: synaesthesia.h:78
void fadePixelWave(int x, int y, int where, int step)
int m_fps
Definition: visualize.h:90
long m_length
Definition: videovisual.h:38
short * m_left
Definition: videovisual.h:36
short * m_right
Definition: videovisual.h:37
unsigned int uint
Definition: compat.h:60
static std::vector< uint32_t > back
Definition: goom_core.cpp:27
static uint32_t * p1
Definition: goom_core.cpp:28
static uint32_t * p2
Definition: goom_core.cpp:28
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static int x1
Definition: mythsocket.cpp:54
static int x2
Definition: mythsocket.cpp:55
None log(str msg, int level=LOGDEBUG)
Definition: xbmc.py:9
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:201
#define lastOutput
#define lastLastOutput
SynaesthesiaFactory SynaesthesiaFactory
#define output
static constexpr size_t LogSize
Definition: synaesthesia.h:13
std::array< double, NumSamples > samp_dbl_array
Definition: synaesthesia.h:17
static constexpr size_t NumSamples
Definition: synaesthesia.h:14
@ Wave
Definition: synaesthesia.h:22
@ Stars
Definition: synaesthesia.h:23
@ Flame
Definition: synaesthesia.h:21
std::array< int, NumSamples > samp_int_array
Definition: synaesthesia.h:18