00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "config.h"
00010 #include <stdint.h>
00011 #include <inttypes.h>
00012
00013
00014 #include <cmath>
00015 #include <cstdlib>
00016
00017
00018 #include <iostream>
00019 using namespace std;
00020
00021
00022 #include <QCoreApplication>
00023 #include <QPainter>
00024 #include <QImage>
00025
00026
00027 #include <compat.h>
00028 #include <mythlogging.h>
00029
00030
00031 #include "mainvisual.h"
00032 #include "synaesthesia.h"
00033
00034 Synaesthesia::Synaesthesia(void) :
00035 m_size(0,0),
00036
00037 m_maxStarRadius(1),
00038 m_fadeMode(Stars),
00039 m_pointsAreDiamonds(true),
00040 m_brightnessTwiddler(0.3),
00041 m_starSize(0.5),
00042
00043 m_outWidth(0),
00044 m_outHeight(0),
00045
00046 m_outputImage(NULL),
00047
00048 m_fgRedSlider(0.0), m_fgGreenSlider(0.5),
00049 m_bgRedSlider(0.75), m_bgGreenSlider(0.4),
00050
00051 m_energy_avg(80.0)
00052 {
00053 m_fps = 29;
00054
00055 coreInit();
00056 setStarSize(m_starSize);
00057 setupPalette();
00058 }
00059
00060 Synaesthesia::~Synaesthesia()
00061 {
00062 if (m_outputImage)
00063 delete m_outputImage;
00064 }
00065
00066 void Synaesthesia::setupPalette(void)
00067 {
00068 #define sBOUND(x) ((x) > 255 ? 255 : (x))
00069 #define sPEAKIFY(x) int(sBOUND((x) - (x)*(255-(x))/255/2))
00070 #define sMAX(x,y) ((x) > (y) ? (x) : (y))
00071 int i;
00072 double scale, fgRed, fgGreen, fgBlue, bgRed, bgGreen, bgBlue;
00073 fgRed = m_fgRedSlider;
00074 fgGreen = m_fgGreenSlider;
00075 fgBlue = 1.0 - sMAX(m_fgRedSlider,m_fgGreenSlider);
00076
00077 scale = (fgRed + fgGreen + fgBlue) / 2.0;
00078 fgRed /= scale;
00079 fgGreen /= scale;
00080 fgBlue /= scale;
00081
00082 bgRed = m_bgRedSlider;
00083 bgGreen = m_bgGreenSlider;
00084 bgBlue = 1.0 - sMAX(m_bgRedSlider,m_bgGreenSlider);
00085
00086 scale = (bgRed + bgGreen + bgBlue) / 2.0;
00087 bgRed /= scale;
00088 bgGreen /= scale;
00089 bgBlue /= scale;
00090
00091 for (i = 0; i < 256; i++) {
00092 int f = i & 15, b = i / 16;
00093
00094
00095
00096
00097 double red = b * bgRed * 16 + f * fgRed * 16;
00098 double green = b * bgGreen * 16 + f * fgGreen * 16;
00099 double blue = b * bgBlue * 16 + f * fgBlue * 16;
00100
00101 double excess = 0.0;
00102 for (int j = 0; j < 5; j++)
00103 {
00104 red += excess / 3;
00105 green += excess / 3;
00106 blue += excess / 3;
00107 excess = 0.0;
00108
00109
00110 if (red > 255) { excess += red - 255; red = 255; }
00111 if (green > 255) { excess += green - 255; green = 255; }
00112 if (blue > 255) { excess += blue - 255; blue = 255; }
00113 }
00114
00115 double scale = (0.5 + (red + green + blue) / 768.0) / 1.5;
00116 red *= scale;
00117 green *= scale;
00118 blue *= scale;
00119
00120 m_palette[i * 3 + 0] = sBOUND(int(red));
00121 m_palette[i * 3 + 1] = sBOUND(int(green));
00122 m_palette[i * 3 + 2] = sBOUND(int(blue));
00123 }
00124 }
00125
00126 void Synaesthesia::resize(const QSize &newsize)
00127 {
00128 m_size = newsize;
00129
00130 m_size.setHeight(m_size.height() / 2);
00131 m_size.setWidth((m_size.width() / 4) * 4);
00132 m_outputBmp.size(m_size.width(), m_size.height());
00133 m_lastOutputBmp.size(m_size.width(), m_size.height());
00134 m_lastLastOutputBmp.size(m_size.width(), m_size.height());
00135 m_outWidth = m_size.width();
00136 m_outHeight = m_size.height();
00137
00138 if (m_outputImage)
00139 delete m_outputImage;
00140
00141 m_size.setHeight(m_size.height() * 2);
00142 m_outputImage = new QImage(m_size, QImage::Format_Indexed8);
00143
00144 if (!m_outputImage)
00145 {
00146 LOG(VB_GENERAL, LOG_ERR,
00147 "outputImage in Synaesthesia::resize() is NULL");
00148 return;
00149 }
00150
00151 for (int i = 0; i < 256; i++)
00152 m_outputImage->setColor(i, qRgba(m_palette[i * 3], m_palette[i * 3 + 1],
00153 m_palette[i * 3 + 2], 255));
00154
00155 #if 0
00156 surface = SDL_SetVideoMode(size.width(), size.height(), 8, 0);
00157
00158 if (!surface)
00159 {
00160 LOG(VB_GENERAL, LOG_ERR, "Couldn't get SDL surface");
00161 return;
00162 }
00163
00164 SDL_Color sdlPalette[256];
00165
00166 for (int i = 0; i < 256; i++)
00167 {
00168 sdlPalette[i].r = m_palette[i * 3];
00169 sdlPalette[i].g = m_palette[i * 3 + 1];
00170 sdlPalette[i].b = m_palette[i * 3 + 2];
00171 }
00172
00173 SDL_SetColors(surface, sdlPalette, 0, 256);
00174 #endif
00175 }
00176
00177 int Synaesthesia::bitReverser(int i)
00178 {
00179 int sum = 0;
00180 for (int j = 0; j < LogSize; j++)
00181 {
00182 sum = (i & 1) + sum * 2;
00183 i >>= 1;
00184 }
00185
00186 return sum;
00187 }
00188
00189 void Synaesthesia::fft(double *x, double *y)
00190 {
00191 int n2 = NumSamples, n1;
00192 for (int twoToTheK = 1; twoToTheK < NumSamples; twoToTheK *= 2)
00193 {
00194 n1 = n2;
00195 n2 /= 2;
00196 for (int j = 0; j < n2; j++)
00197 {
00198 double c = m_cosTable[j * twoToTheK & (NumSamples - 1)],
00199 s = m_negSinTable[j * twoToTheK & (NumSamples - 1)];
00200 for (int i = j; i < NumSamples; i += n1)
00201 {
00202 int l = i + n2;
00203 double xt = x[i] - x[l];
00204 x[i] = (x[i] + x[l]);
00205 double yt = y[i] - y[l];
00206 y[i] = (y[i] + y[l]);
00207 x[l] = xt * c - yt * s;
00208 y[l] = xt * s + yt * c;
00209 }
00210 }
00211 }
00212 }
00213
00214 void Synaesthesia::setStarSize(double lsize)
00215 {
00216 double fadeModeFudge = (m_fadeMode == Wave ? 0.4 :
00217 (m_fadeMode == Flame ? 0.6 : 0.78));
00218
00219 int factor;
00220 if (lsize > 0.0)
00221 factor = int(exp(log(fadeModeFudge) / (lsize * 8.0)) * 255);
00222 else
00223 factor = 0;
00224
00225 if (factor > 255)
00226 factor = 255;
00227
00228 for (int i = 0; i < 256; i++)
00229 m_scaleDown[i] = i * factor>>8;
00230
00231 m_maxStarRadius = 1;
00232 for (int i = 255; i; i = m_scaleDown[i])
00233 m_maxStarRadius++;
00234 }
00235
00236 void Synaesthesia::coreInit(void)
00237 {
00238 for (int i = 0; i < NumSamples; i++)
00239 {
00240 m_negSinTable[i] = -sin(3.141592 * 2.0 / NumSamples * i);
00241 m_cosTable[i] = cos(3.141592 * 2.0 / NumSamples * i);
00242 m_bitReverse[i] = bitReverser(i);
00243 }
00244 }
00245
00246 #define output ((unsigned char*)m_outputBmp.data)
00247 #define lastOutput ((unsigned char*)m_lastOutputBmp.data)
00248 #define lastLastOutput ((unsigned char*)m_lastLastOutputBmp.data)
00249
00250 void Synaesthesia::addPixel(int x, int y, int br1, int br2)
00251 {
00252 if (x < 0 || x > m_outWidth || y < 0 || y >= m_outHeight)
00253 return;
00254
00255 unsigned char *p = output + x * 2 + y * m_outWidth * 2;
00256 if (p[0] + br1 < 255)
00257 p[0] += br1;
00258 else
00259 p[0] = 255;
00260 if (p[1] + br2 < 255)
00261 p[1] += br2;
00262 else
00263 p[1] = 255;
00264 }
00265
00266 void Synaesthesia::addPixelFast(unsigned char *p, int br1, int br2)
00267 {
00268 if (p[0] + br1 < 255)
00269 p[0] += br1;
00270 else
00271 p[0] = 255;
00272 if (p[1] + br2 < 255)
00273 p[1] += br2;
00274 else
00275 p[1] = 255;
00276 }
00277
00278 unsigned char Synaesthesia::getPixel(int x, int y, int where)
00279 {
00280 if (x < 0 || y < 0 || x >= m_outWidth || y >= m_outHeight)
00281 return 0;
00282
00283 return lastOutput[where];
00284 }
00285
00286 void Synaesthesia::fadeFade(void)
00287 {
00288 register uint32_t *ptr = (uint32_t *)output;
00289 int i = m_outWidth * m_outHeight * 2 / sizeof(uint32_t);
00290 do {
00291 uint32_t x = *ptr;
00292 if (x)
00293 *(ptr++) = x - ((x & (uintptr_t)0xf0f0f0f0) >> 4) -
00294 ((x & (uintptr_t)0xe0e0e0e0) >> 5);
00295 else
00296 ptr++;
00297 } while (--i > 0);
00298 }
00299
00300 void Synaesthesia::fadePixelWave(int x, int y, int where, int step)
00301 {
00302 short j = short((int(getPixel(x - 1, y, where - 2)) +
00303 int(getPixel(x + 1, y, where + 2)) +
00304 int(getPixel(x, y - 1, where - step)) +
00305 int(getPixel(x, y + 1, where + step))) >> 2) +
00306 lastOutput[where];
00307
00308 if (!j)
00309 {
00310 output[where] = 0;
00311 return;
00312 }
00313 j = j - lastLastOutput[where] - 1;
00314 if (j < 0)
00315 output[where] = 0;
00316 else if (j & (255 * 256))
00317 output[where] = 255;
00318 else
00319 output[where] = j;
00320 }
00321
00322 void Synaesthesia::fadeWave(void)
00323 {
00324 unsigned short *t = m_lastLastOutputBmp.data;
00325 m_lastLastOutputBmp.data = m_lastOutputBmp.data;
00326 m_lastOutputBmp.data = m_outputBmp.data;
00327 m_outputBmp.data = t;
00328
00329 int x, y, i, j, start, end;
00330 int step = m_outWidth*2;
00331 for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
00332 x < m_outWidth; x++, i += 2, j += 2)
00333 {
00334 fadePixelWave(x, 0, i, step);
00335 fadePixelWave(x, 0, i + 1, step);
00336 fadePixelWave(x, m_outHeight - 1, j, step);
00337 fadePixelWave(x, m_outHeight - 1, j + 1, step);
00338 }
00339
00340 for (y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2;
00341 y < m_outHeight; y++, i += step, j += step)
00342 {
00343 fadePixelWave(0, y, i, step);
00344 fadePixelWave(0, y, i + 1, step);
00345 fadePixelWave(m_outWidth - 1, y, j, step);
00346 fadePixelWave(m_outWidth - 1, y, j + 1, step);
00347 }
00348
00349 for (y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2;
00350 y < m_outHeight - 1; y++, start += step, end += step)
00351 {
00352 int i = start;
00353 do
00354 {
00355 short j = short((int(lastOutput[i - 2]) +
00356 int(lastOutput[i + 2]) +
00357 int(lastOutput[i - step]) +
00358 int(lastOutput[i + step])) >> 2) +
00359 lastOutput[i];
00360 if (!j)
00361 {
00362 output[i] = 0;
00363 }
00364 else
00365 {
00366 j = j - lastLastOutput[i] - 1;
00367 if (j < 0)
00368 output[i] = 0;
00369 else if (j & (255*256))
00370 output[i] = 255;
00371 else
00372 output[i] = j;
00373 }
00374 } while(++i < end);
00375 }
00376 }
00377
00378 void Synaesthesia::fadePixelHeat(int x, int y, int where, int step)
00379 {
00380 short j = short((int(getPixel(x - 1, y, where - 2)) +
00381 int(getPixel(x + 1, y, where + 2)) +
00382 int(getPixel(x, y - 1, where - step)) +
00383 int(getPixel(x, y + 1, where + step))) >> 2) +
00384 lastOutput[where];
00385 if (!j)
00386 {
00387 output[where] = 0;
00388 return;
00389 }
00390 j = j -lastLastOutput[where] - 1;
00391 if (j < 0)
00392 output[where] = 0;
00393 else if (j & (255 * 256))
00394 output[where] = 255;
00395 else
00396 output[where] = j;
00397 }
00398
00399 void Synaesthesia::fadeHeat(void)
00400 {
00401 unsigned short *t = m_lastLastOutputBmp.data;
00402 m_lastLastOutputBmp.data = m_lastOutputBmp.data;
00403 m_lastOutputBmp.data = m_outputBmp.data;
00404 m_outputBmp.data = t;
00405
00406 int x, y, i, j, start, end;
00407 int step = m_outWidth * 2;
00408 for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2;
00409 x < m_outWidth; x++, i += 2, j += 2)
00410 {
00411 fadePixelHeat(x, 0, i, step);
00412 fadePixelHeat(x, 0, i + 1, step);
00413 fadePixelHeat(x, m_outHeight - 1, j, step);
00414 fadePixelHeat(x, m_outHeight - 1, j + 1, step);
00415 }
00416
00417 for(y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2; y < m_outHeight;
00418 y++, i += step, j += step)
00419 {
00420 fadePixelHeat(0, y, i, step);
00421 fadePixelHeat(0, y, i + 1, step);
00422 fadePixelHeat(m_outWidth - 1, y, j, step);
00423 fadePixelHeat(m_outWidth - 1, y, j + 1, step);
00424 }
00425
00426 for(y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2;
00427 y < m_outHeight - 1; y++, start += step, end += step)
00428 {
00429 int i = start;
00430 do
00431 {
00432 short j = short((int(lastOutput[i - 2]) +
00433 int(lastOutput[i + 2]) +
00434 int(lastOutput[i - step]) +
00435 int(lastOutput[i + step])) >> 2) +
00436 lastOutput[i];
00437 if (!j)
00438 output[i] = 0;
00439 else
00440 {
00441 j = j - lastLastOutput[i] +
00442 ((lastLastOutput[i] - lastOutput[i]) >> 2) - 1;
00443 if (j < 0)
00444 output[i] = 0;
00445 else if (j & (255*256))
00446 output[i] = 255;
00447 else
00448 output[i] = j;
00449 }
00450 } while(++i < end);
00451 }
00452 }
00453
00454 void Synaesthesia::fade(void)
00455 {
00456 switch(m_fadeMode)
00457 {
00458 case Stars: fadeFade(); break;
00459 case Flame: fadeHeat(); break;
00460 case Wave: fadeWave(); break;
00461 default: break;
00462 }
00463 }
00464
00465 bool Synaesthesia::process(VisualNode *node)
00466 {
00467 fade();
00468
00469 if (!node)
00470 return false;
00471
00472 double x[NumSamples], y[NumSamples];
00473 double a[NumSamples], b[NumSamples];
00474 double energy;
00475 int clarity[NumSamples];
00476 int i, j, k;
00477
00478 int brightFactor = int(Brightness * m_brightnessTwiddler / (m_starSize + 0.01));
00479
00480 int numSamps = NumSamples;
00481 if (node->length < NumSamples)
00482 numSamps = node->length;
00483
00484 memset(x, 0, sizeof(x));
00485 memset(y, 0, sizeof(y));
00486
00487 for (i = 0; i < numSamps; i++)
00488 {
00489 x[i] = node->left[i];
00490 if (node->right)
00491 y[i] = node->right[i];
00492 else
00493 y[i] = x[i];
00494 }
00495
00496 fft(x, y);
00497
00498 energy = 0.0;
00499
00500 for (i = 0 + 1; i < NumSamples / 2; i++)
00501 {
00502 double x1 = x[m_bitReverse[i]],
00503 y1 = y[m_bitReverse[i]],
00504 x2 = x[m_bitReverse[NumSamples - i]],
00505 y2 = y[m_bitReverse[NumSamples - i]],
00506 aa, bb;
00507 a[i] = sqrt(aa = (x1 + x2) * (x1 + x2) + (y1 - y2) * (y1 - y2));
00508 b[i] = sqrt(bb = (x1 - x2) * (x1 - x2) + (y2 + y2) * (y1 + y2));
00509 if (aa + bb != 0.0)
00510 clarity[i] = (int)(((x1 + x2) * (x1 - x2) + (y1 + y2) * (y1 - y2)) /
00511 (aa + bb) * 256);
00512 else
00513 clarity[i] = 0;
00514
00515 energy += (aa + bb) * i * i;
00516 }
00517
00518 energy = sqrt(energy / NumSamples) / 65536.0;
00519
00520
00521
00522
00523
00524 double brightFactor2 = (brightFactor / 65536.0 / NumSamples) *
00525 sqrt(m_outHeight * m_outWidth / (320.0 * 200.0));
00526
00527 m_energy_avg = m_energy_avg * 0.95 + energy * 0.05;
00528 if (m_energy_avg > 0.0)
00529 brightFactor2 *= 80.0 / (m_energy_avg + 5.0);
00530
00531 for (i = 1; i < NumSamples / 2; i++)
00532 {
00533 if (a[i] > 0 || b[i] > 0)
00534 {
00535 int h = (int)(b[i] * m_outWidth / (a[i] + b[i]));
00536 int br1, br2, br = (int)((a[i] + b[i]) * i * brightFactor2);
00537 br1 = br * (clarity[i] + 128) >> 8;
00538 br2 = br * (128 - clarity[i]) >> 8;
00539 if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255;
00540 if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255;
00541
00542 int px = h,
00543 py = m_outHeight - i * m_outHeight / (NumSamples / 2);
00544
00545 if (m_pointsAreDiamonds)
00546 {
00547 addPixel(px, py, br1, br2);
00548 br1 = m_scaleDown[br1];
00549 br2 = m_scaleDown[br2];
00550
00551 for(j = 1; br1 > 0 || br2 > 0;
00552 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
00553 {
00554 for (k = 0; k < j; k++)
00555 {
00556 addPixel(px - j + k,py - k, br1, br2);
00557 addPixel(px + k, py - j + k, br1, br2);
00558 addPixel(px + j - k, py + k, br1, br2);
00559 addPixel(px - k, py + j - k, br1, br2);
00560 }
00561 }
00562 }
00563 else
00564 {
00565 if (px < m_maxStarRadius || py < m_maxStarRadius ||
00566 px > m_outWidth - m_maxStarRadius ||
00567 py > m_outHeight - m_maxStarRadius)
00568 {
00569 addPixel(px, py, br1, br2);
00570 for( j = 1; br1 > 0 || br2 > 0;
00571 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
00572 {
00573 addPixel(px + j, py, br1, br2);
00574 addPixel(px, py + j, br1, br2);
00575 addPixel(px - j, py, br1, br2);
00576 addPixel(px, py - j, br1, br2);
00577 }
00578 }
00579 else
00580 {
00581 unsigned char *p = output + px * 2 + py * m_outWidth * 2,
00582 *p1 = p, *p2 = p, *p3 = p, *p4 = p;
00583 addPixelFast(p, br1, br2);
00584 for (; br1 > 0 || br2 > 0;
00585 br1 = m_scaleDown[br1], br2 = m_scaleDown[br2])
00586 {
00587 p1 += 2;
00588 addPixelFast(p1, br1, br2);
00589 p2 -= 2;
00590 addPixelFast(p2, br1, br2);
00591 p3 += m_outWidth * 2;
00592 addPixelFast(p3, br1, br2);
00593 p4 -= m_outWidth*2;
00594 addPixelFast(p4, br1, br2);
00595 }
00596 }
00597 }
00598 }
00599 }
00600
00601 return false;
00602 }
00603
00604 bool Synaesthesia::draw(QPainter *p, const QColor &back)
00605 {
00606 if (!m_outputImage)
00607 return true;
00608
00609 (void)back;
00610
00611 register uint32_t *ptrOutput = (uint32_t *)output;
00612
00613 for (int j = 0; j < m_outHeight * 2; j += 2)
00614 {
00615 uint32_t *ptrTop = (uint32_t *)(m_outputImage->scanLine(j));
00616 uint32_t *ptrBot = (uint32_t *)(m_outputImage->scanLine(j+1));
00617
00618 int i = m_outWidth / 4;
00619
00620 do
00621 {
00622 register unsigned int const r1 = *(ptrOutput++);
00623 register unsigned int const r2 = *(ptrOutput++);
00624
00625 register unsigned int const v = ((r1 & 0x000000f0ul) >> 4) |
00626 ((r1 & 0x0000f000ul) >> 8) |
00627 ((r1 & 0x00f00000ul) >> 12) |
00628 ((r1 & 0xf0000000ul) >> 16);
00629
00630 *(ptrTop++) = v | (((r2 & 0x000000f0ul) << 12) |
00631 ((r2 & 0x0000f000ul) << 8) |
00632 ((r2 & 0x00f00000ul) << 4) |
00633 ((r2 & 0xf0000000ul)));
00634
00635 *(ptrBot++) = v | (((r2 & 0x000000f0ul) << 12) |
00636 ((r2 & 0x0000f000ul) << 8) |
00637 ((r2 & 0x00f00000ul) << 4) |
00638 ((r2 & 0xf0000000ul)));
00639 } while (--i);
00640 }
00641
00642 p->drawImage(0, 0, *m_outputImage);
00643
00644 return true;
00645 }
00646
00647 static class SynaesthesiaFactory : public VisFactory
00648 {
00649 public:
00650 const QString &name(void) const
00651 {
00652 static QString name = QCoreApplication::translate("Visualizers",
00653 "Synaesthesia");
00654 return name;
00655 }
00656
00657 uint plugins(QStringList *list) const
00658 {
00659 *list << name();
00660 return 1;
00661 }
00662
00663 VisualBase *create(MainVisual *parent, const QString &pluginName) const
00664 {
00665 (void)parent;
00666 (void)pluginName;
00667 return new Synaesthesia();
00668 }
00669 } SynaesthesiaFactory;