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  int i;
52  double scale, fgRed, fgGreen, fgBlue, bgRed, bgGreen, bgBlue;
53  fgRed = m_fgRedSlider;
54  fgGreen = m_fgGreenSlider;
55  fgBlue = 1.0 - sMAX(m_fgRedSlider,m_fgGreenSlider);
56  //scale = sMAX(sMAX(fgRed,fgGreen),fgBlue);
57  scale = (fgRed + fgGreen + fgBlue) / 2.0;
58  fgRed /= scale;
59  fgGreen /= scale;
60  fgBlue /= scale;
61 
62  bgRed = m_bgRedSlider;
63  bgGreen = m_bgGreenSlider;
64  bgBlue = 1.0 - sMAX(m_bgRedSlider,m_bgGreenSlider);
65  //scale = sMAX(sMAX(bgRed,bgGreen),bgBlue);
66  scale = (bgRed + bgGreen + bgBlue) / 2.0;
67  bgRed /= scale;
68  bgGreen /= scale;
69  bgBlue /= scale;
70 
71  for (i = 0; i < 256; i++) {
72  int f = i & 15, b = i / 16;
73  //palette[i * 3 + 0] = sPEAKIFY(b*bgRed*16+f*fgRed*16);
74  //palette[i * 3 + 1] = sPEAKIFY(b*bgGreen*16+f*fgGreen*16);
75  //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] = sBOUND(int(red));
101  m_palette[i * 3 + 1] = sBOUND(int(green));
102  m_palette[i * 3 + 2] = sBOUND(int(blue));
103  }
104 }
105 
106 void 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 (int 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 (int j = 0; j < LogSize; j++)
160  {
161  sum = (i & 1) + sum * 2;
162  i >>= 1;
163  }
164 
165  return sum;
166 }
167 
168 void Synaesthesia::fft(double *x, double *y)
169 {
170  int n2 = NumSamples;
171  for (int twoToTheK = 1; twoToTheK < NumSamples; twoToTheK *= 2)
172  {
173  int n1 = n2;
174  n2 /= 2;
175  for (int j = 0; j < n2; j++)
176  {
177  double c = m_cosTable[j * twoToTheK & (NumSamples - 1)],
178  s = m_negSinTable[j * twoToTheK & (NumSamples - 1)];
179  for (int i = j; i < NumSamples; i += n1)
180  {
181  int 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 
193 void Synaesthesia::setStarSize(double lsize)
194 {
195  double fadeModeFudge = (m_fadeMode == Wave ? 0.4 :
196  (m_fadeMode == Flame ? 0.6 : 0.78));
197 
198  int factor;
199  if (lsize > 0.0)
200  factor = int(exp(log(fadeModeFudge) / (lsize * 8.0)) * 255);
201  else
202  factor = 0;
203 
204  if (factor > 255)
205  factor = 255;
206 
207  for (int i = 0; i < 256; i++)
208  m_scaleDown[i] = i * factor>>8;
209 
210  m_maxStarRadius = 1;
211  for (int i = 255; i; i = m_scaleDown[i])
212  m_maxStarRadius++;
213 }
214 
216 {
217  for (int i = 0; i < NumSamples; i++)
218  {
219  m_negSinTable[i] = -sin(3.141592 * 2.0 / NumSamples * i);
220  m_cosTable[i] = cos(3.141592 * 2.0 / NumSamples * i);
221  m_bitReverse[i] = bitReverser(i);
222  }
223 }
224 
225 #define output ((unsigned char*)m_outputBmp.data)
226 #define lastOutput ((unsigned char*)m_lastOutputBmp.data)
227 #define lastLastOutput ((unsigned char*)m_lastLastOutputBmp.data)
228 
229 void Synaesthesia::addPixel(int x, int y, int br1, int br2)
230 {
231  if (x < 0 || x > m_outWidth || y < 0 || y >= m_outHeight)
232  return;
233 
234  unsigned char *p = output + x * 2 + 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 
245 void 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 
257 unsigned char Synaesthesia::getPixel(int x, int y, int where)
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  uint32_t *ptr = (uint32_t *)output;
268  int i = m_outWidth * m_outHeight * 2 / sizeof(uint32_t);
269  do {
270  uint32_t x = *ptr;
271  if (x)
272  *(ptr++) = x - ((x & (uintptr_t)0xf0f0f0f0) >> 4) -
273  ((x & (uintptr_t)0xe0e0e0e0) >> 5);
274  else
275  ptr++;
276  } while (--i > 0);
277 }
278 
279 void Synaesthesia::fadePixelWave(int x, int y, int where, int step)
280 {
281  short j = short((int(getPixel(x - 1, y, where - 2)) +
282  int(getPixel(x + 1, y, where + 2)) +
283  int(getPixel(x, y - 1, where - step)) +
284  int(getPixel(x, y + 1, where + step))) >> 2) +
285  lastOutput[where];
286 
287  if (!j)
288  {
289  output[where] = 0;
290  return;
291  }
292  j = j - lastLastOutput[where] - 1;
293  if (j < 0)
294  output[where] = 0;
295  else if (j & (255 * 256))
296  output[where] = 255;
297  else
298  output[where] = j;
299 }
300 
302 {
303  unsigned short *t = m_lastLastOutputBmp.data;
304  m_lastLastOutputBmp.data = m_lastOutputBmp.data;
305  m_lastOutputBmp.data = m_outputBmp.data;
306  m_outputBmp.data = t;
307 
308  int x, y, i, j, start, end;
309  int step = m_outWidth*2;
310  for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
311  x < m_outWidth; x++, i += 2, j += 2)
312  {
313  fadePixelWave(x, 0, i, step);
314  fadePixelWave(x, 0, i + 1, step);
315  fadePixelWave(x, m_outHeight - 1, j, step);
316  fadePixelWave(x, m_outHeight - 1, j + 1, step);
317  }
318 
319  for (y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2;
320  y < m_outHeight; y++, i += step, j += step)
321  {
322  fadePixelWave(0, y, i, step);
323  fadePixelWave(0, y, i + 1, step);
324  fadePixelWave(m_outWidth - 1, y, j, step);
325  fadePixelWave(m_outWidth - 1, y, j + 1, step);
326  }
327 
328  for (y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2;
329  y < m_outHeight - 1; y++, start += step, end += step)
330  {
331  int i2 = start;
332  do
333  {
334  short j2 = short((int(lastOutput[i2 - 2]) +
335  int(lastOutput[i2 + 2]) +
336  int(lastOutput[i2 - step]) +
337  int(lastOutput[i2 + step])) >> 2) +
338  lastOutput[i2];
339  if (!j2)
340  {
341  output[i2] = 0;
342  }
343  else
344  {
345  j2 = j2 - lastLastOutput[i2] - 1;
346  if (j2 < 0)
347  output[i2] = 0;
348  else if (j2 & (255*256))
349  output[i2] = 255;
350  else
351  output[i2] = j2;
352  }
353  } while(++i2 < end);
354  }
355 }
356 
357 void Synaesthesia::fadePixelHeat(int x, int y, int where, int step)
358 {
359  short j = short((int(getPixel(x - 1, y, where - 2)) +
360  int(getPixel(x + 1, y, where + 2)) +
361  int(getPixel(x, y - 1, where - step)) +
362  int(getPixel(x, y + 1, where + step))) >> 2) +
363  lastOutput[where];
364  if (!j)
365  {
366  output[where] = 0;
367  return;
368  }
369  j = j -lastLastOutput[where] - 1;
370  if (j < 0)
371  output[where] = 0;
372  else if (j & (255 * 256))
373  output[where] = 255;
374  else
375  output[where] = j;
376 }
377 
379 {
380  unsigned short *t = m_lastLastOutputBmp.data;
381  m_lastLastOutputBmp.data = m_lastOutputBmp.data;
382  m_lastOutputBmp.data = m_outputBmp.data;
383  m_outputBmp.data = t;
384 
385  int x, y, i, j, start, end;
386  int step = m_outWidth * 2;
387  for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
388  x < m_outWidth; x++, i += 2, j += 2)
389  {
390  fadePixelHeat(x, 0, i, step);
391  fadePixelHeat(x, 0, i + 1, step);
392  fadePixelHeat(x, m_outHeight - 1, j, step);
393  fadePixelHeat(x, m_outHeight - 1, j + 1, step);
394  }
395 
396  for(y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2; y < m_outHeight;
397  y++, i += step, j += step)
398  {
399  fadePixelHeat(0, y, i, step);
400  fadePixelHeat(0, y, i + 1, step);
401  fadePixelHeat(m_outWidth - 1, y, j, step);
402  fadePixelHeat(m_outWidth - 1, y, j + 1, step);
403  }
404 
405  for(y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2;
406  y < m_outHeight - 1; y++, start += step, end += step)
407  {
408  int i2 = start;
409  do
410  {
411  short j2 = short((int(lastOutput[i2 - 2]) +
412  int(lastOutput[i2 + 2]) +
413  int(lastOutput[i2 - step]) +
414  int(lastOutput[i2 + step])) >> 2) +
415  lastOutput[i2];
416  if (!j2)
417  output[i2] = 0;
418  else
419  {
420  j2 = j2 - lastLastOutput[i2] +
421  ((lastLastOutput[i2] - lastOutput[i2]) >> 2) - 1;
422  if (j2 < 0)
423  output[i2] = 0;
424  else if (j2 & (255*256))
425  output[i2] = 255;
426  else
427  output[i2] = j2;
428  }
429  } while(++i2 < end);
430  }
431 }
432 
434 {
435  switch(m_fadeMode)
436  {
437  case Stars: fadeFade(); break;
438  case Flame: fadeHeat(); break;
439  case Wave: fadeWave(); break;
440  default: break;
441  }
442 }
443 
445 {
446  fade();
447 
448  if (!node)
449  return false;
450 
451  double x[NumSamples], y[NumSamples];
452  double a[NumSamples], b[NumSamples];
453  double energy;
454  int clarity[NumSamples];
455  int i, j, k;
456 
457  int brightFactor = int(Brightness * m_brightnessTwiddler / (m_starSize + 0.01));
458 
459  int numSamps = NumSamples;
460  if (node->m_length < NumSamples)
461  numSamps = node->m_length;
462 
463  memset(x, 0, sizeof(x));
464  memset(y, 0, sizeof(y));
465 
466  for (i = 0; i < numSamps; i++)
467  {
468  x[i] = node->m_left[i];
469  if (node->m_right)
470  y[i] = node->m_right[i];
471  else
472  y[i] = x[i];
473  }
474 
475  fft(x, y);
476 
477  energy = 0.0;
478 
479  for (i = 0 + 1; i < NumSamples / 2; i++)
480  {
481  double x1 = x[m_bitReverse[i]],
482  y1 = y[m_bitReverse[i]],
483  x2 = x[m_bitReverse[NumSamples - i]],
484  y2 = y[m_bitReverse[NumSamples - i]],
485  aa, bb;
486  a[i] = sqrt(aa = (x1 + x2) * (x1 + x2) + (y1 - y2) * (y1 - y2));
487  b[i] = sqrt(bb = (x1 - x2) * (x1 - x2) + (y2 + y2) * (y1 + y2));
488  if (aa + bb != 0.0)
489  clarity[i] = (int)(((x1 + x2) * (x1 - x2) + (y1 + y2) * (y1 - y2)) /
490  (aa + bb) * 256);
491  else
492  clarity[i] = 0;
493 
494  energy += (aa + bb) * i * i;
495  }
496 
497  energy = sqrt(energy / NumSamples) / 65536.0;
498 
499  //int heightFactor = NumSamples / 2 / outHeight + 1;
500  //int actualHeight = NumSamples / 2 / heightFactor;
501  //int heightAdd = (outHeight + actualHeight) >> 1;
502 
503  double brightFactor2 = (brightFactor / 65536.0 / NumSamples) *
504  sqrt(m_outHeight * m_outWidth / (320.0 * 200.0));
505 
506  m_energy_avg = m_energy_avg * 0.95 + energy * 0.05;
507  if (m_energy_avg > 0.0)
508  brightFactor2 *= 80.0 / (m_energy_avg + 5.0);
509 
510  for (i = 1; i < NumSamples / 2; i++)
511  {
512  if (a[i] > 0 || b[i] > 0)
513  {
514  int h = (int)(b[i] * m_outWidth / (a[i] + b[i]));
515  int br1, br2, br = (int)((a[i] + b[i]) * i * brightFactor2);
516  br1 = br * (clarity[i] + 128) >> 8;
517  br2 = br * (128 - clarity[i]) >> 8;
518  if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255;
519  if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255;
520 
521  int px = h,
522  py = m_outHeight - i * m_outHeight / (NumSamples / 2);
523 
524  if (m_pointsAreDiamonds)
525  {
526  addPixel(px, py, br1, br2);
527  br1 = m_scaleDown[br1];
528  br2 = m_scaleDown[br2];
529 
530  for(j = 1; br1 > 0 || br2 > 0;
531  j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
532  {
533  for (k = 0; k < j; k++)
534  {
535  addPixel(px - j + k,py - k, br1, br2);
536  addPixel(px + k, py - j + k, br1, br2);
537  addPixel(px + j - k, py + k, br1, br2);
538  addPixel(px - k, py + j - k, br1, br2);
539  }
540  }
541  }
542  else
543  {
544  if (px < m_maxStarRadius || py < m_maxStarRadius ||
545  px > m_outWidth - m_maxStarRadius ||
546  py > m_outHeight - m_maxStarRadius)
547  {
548  addPixel(px, py, br1, br2);
549  for( j = 1; br1 > 0 || br2 > 0;
550  j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
551  {
552  addPixel(px + j, py, br1, br2);
553  addPixel(px, py + j, br1, br2);
554  addPixel(px - j, py, br1, br2);
555  addPixel(px, py - j, br1, br2);
556  }
557  }
558  else
559  {
560  unsigned char *p = output + px * 2 + py * m_outWidth * 2,
561  *p1 = p, *p2 = p, *p3 = p, *p4 = p;
562  addPixelFast(p, br1, br2);
563  for (; br1 > 0 || br2 > 0;
564  br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
565  {
566  p1 += 2;
567  addPixelFast(p1, br1, br2);
568  p2 -= 2;
569  addPixelFast(p2, br1, br2);
570  p3 += m_outWidth * 2;
571  addPixelFast(p3, br1, br2);
572  p4 -= m_outWidth*2;
573  addPixelFast(p4, br1, br2);
574  }
575  }
576  }
577  }
578  }
579 
580  return false;
581 }
582 
583 bool Synaesthesia::draw(QPainter *p, const QColor &back)
584 {
585  if (!m_outputImage)
586  return true;
587 
588  (void)back;
589 
590  uint32_t *ptrOutput = (uint32_t *)output;
591 
592  for (int j = 0; j < m_outHeight * 2; j += 2)
593  {
594  uint32_t *ptrTop = (uint32_t *)(m_outputImage->scanLine(j));
595  uint32_t *ptrBot = (uint32_t *)(m_outputImage->scanLine(j+1));
596 
597  int i = m_outWidth / 4;
598 
599  do
600  {
601  unsigned int const r1 = *(ptrOutput++);
602  unsigned int const r2 = *(ptrOutput++);
603 
604  unsigned int const v = ((r1 & 0x000000f0UL) >> 4) |
605  ((r1 & 0x0000f000UL) >> 8) |
606  ((r1 & 0x00f00000UL) >> 12) |
607  ((r1 & 0xf0000000UL) >> 16);
608 
609  *(ptrTop++) = v | (((r2 & 0x000000f0UL) << 12) |
610  ((r2 & 0x0000f000UL) << 8) |
611  ((r2 & 0x00f00000UL) << 4) |
612  ((r2 & 0xf0000000UL)));
613 
614  *(ptrBot++) = v | (((r2 & 0x000000f0UL) << 12) |
615  ((r2 & 0x0000f000UL) << 8) |
616  ((r2 & 0x00f00000UL) << 4) |
617  ((r2 & 0xf0000000UL)));
618  } while (--i > 0);
619  }
620 
621  p->drawImage(0, 0, *m_outputImage);
622 
623  return true;
624 }
625 
626 static class SynaesthesiaFactory : public VisFactory
627 {
628  public:
629  const QString &name(void) const override // VisFactory
630  {
631  static QString name = QCoreApplication::translate("Visualizers",
632  "Synaesthesia");
633  return name;
634  }
635 
636  uint plugins(QStringList *list) const override // VisFactory
637  {
638  *list << name();
639  return 1;
640  }
641 
642  VisualBase *create(MainVisual *parent, const QString &pluginName) const override // VisFactory
643  {
644  (void)parent;
645  (void)pluginName;
646  return new Synaesthesia();
647  }
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)
unsigned int uint
Definition: compat.h:140
uint plugins(QStringList *list) const override
short * m_right
Definition: videovisual.h:34
#define NumSamples
Definition: synaesthesia.h:13
unsigned char b
Definition: ParseText.cpp:329
bool draw(QPainter *p, const QColor &back) override
unsigned char getPixel(int x, int y, int where)
static int x2
Definition: mythsocket.cpp:61
void addPixelFast(unsigned char *p, int br1, int br2)
unsigned char t
Definition: ParseText.cpp:329
SynaesthesiaFactory SynaesthesiaFactory
#define Stars
Definition: synaesthesia.h:17
def log(debug, txt)
Definition: utilities.py:5
int bitReverser(int i)
#define LogSize
Definition: synaesthesia.h:11
#define sBOUND(x)
const char * name
Definition: ParseText.cpp:328
#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