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