00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include <cmath>
00014
00015
00016 #include <iostream>
00017 using namespace std;
00018
00019
00020 #include <QCoreApplication>
00021 #include <QPainter>
00022 #include <QImage>
00023
00024
00025 #include <mythdbcon.h>
00026 #include <mythcontext.h>
00027 #include <mythuihelper.h>
00028
00029
00030 #include "mainvisual.h"
00031 #include "visualize.h"
00032 #include "inlines.h"
00033 #include "decoder.h"
00034 #include "metadata.h"
00035 #include "musicplayer.h"
00036
00037 #define FFTW_N 512
00038
00039
00040
00041 VisFactory* VisFactory::g_pVisFactories = 0;
00042
00043 VisualBase::VisualBase(bool screensaverenable)
00044 : m_fps(20), m_xscreensaverenable(screensaverenable)
00045 {
00046 if (!m_xscreensaverenable)
00047 GetMythUI()->DoDisableScreensaver();
00048 }
00049
00050 VisualBase::~VisualBase()
00051 {
00052
00053
00054
00055
00056
00057 if (!m_xscreensaverenable)
00058 GetMythUI()->DoRestoreScreensaver();
00059 }
00060
00061
00062 void VisualBase::drawWarning(QPainter *p, const QColor &back, const QSize &size, QString warning, int fontSize)
00063 {
00064 p->fillRect(0, 0, size.width(), size.height(), back);
00065 p->setPen(Qt::white);
00066 QFont font = GetMythUI()->GetMediumFont();
00067 font.setPointSizeF(fontSize * (size.width() / 800.0));
00068 p->setFont(font);
00069
00070 p->drawText(0, 0, size.width(), size.height(), Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextWordWrap, warning);
00071 }
00072
00074
00075
00076 LogScale::LogScale(int maxscale, int maxrange)
00077 : indices(0), s(0), r(0)
00078 {
00079 setMax(maxscale, maxrange);
00080 }
00081
00082 LogScale::~LogScale()
00083 {
00084 if (indices)
00085 delete [] indices;
00086 }
00087
00088 void LogScale::setMax(int maxscale, int maxrange)
00089 {
00090 if (maxscale == 0 || maxrange == 0)
00091 return;
00092
00093 s = maxscale;
00094 r = maxrange;
00095
00096 if (indices)
00097 delete [] indices;
00098
00099 double alpha;
00100 int i, scaled;
00101 long double domain = (long double) maxscale;
00102 long double range = (long double) maxrange;
00103 long double x = 1.0;
00104 long double dx = 1.0;
00105 long double y = 0.0;
00106 long double yy = 0.0;
00107 long double t = 0.0;
00108 long double e4 = 1.0E-8;
00109
00110 indices = new int[maxrange];
00111 for (i = 0; i < maxrange; i++)
00112 indices[i] = 0;
00113
00114
00115 for (uint i=0; i<10000 && (std::abs(dx) > e4); i++)
00116 {
00117 t = std::log((domain + x) / x);
00118 y = (x * t) - range;
00119 yy = t - (domain / (x + domain));
00120 dx = y / yy;
00121 x -= dx;
00122 }
00123
00124 alpha = x;
00125 for (i = 1; i < (int) domain; i++)
00126 {
00127 scaled = (int) floor(0.5 + (alpha * log((double(i) + alpha) / alpha)));
00128 if (scaled < 1)
00129 scaled = 1;
00130 if (indices[scaled - 1] < i)
00131 indices[scaled - 1] = i;
00132 }
00133 }
00134
00135 int LogScale::operator[](int index)
00136 {
00137 return indices[index];
00138 }
00139
00141
00142
00143 #define RUBBERBAND 0
00144 #define TWOCOLOUR 0
00145 StereoScope::StereoScope() :
00146 startColor(Qt::green), targetColor(Qt::red),
00147 rubberband(RUBBERBAND), falloff(1.0)
00148 {
00149 m_fps = 45;
00150 }
00151
00152 StereoScope::~StereoScope()
00153 {
00154 }
00155
00156 void StereoScope::resize( const QSize &newsize )
00157 {
00158 size = newsize;
00159
00160 uint os = magnitudes.size();
00161 magnitudes.resize( size.width() * 2 );
00162 for ( ; os < magnitudes.size(); os++ )
00163 magnitudes[os] = 0.0;
00164 }
00165
00166 bool StereoScope::process( VisualNode *node )
00167 {
00168 bool allZero = true;
00169
00170
00171 if (node)
00172 {
00173 double index = 0;
00174 double const step = (double)SAMPLES_DEFAULT_SIZE / size.width();
00175 for ( int i = 0; i < size.width(); i++)
00176 {
00177 unsigned long indexTo = (unsigned long)(index + step);
00178 if (indexTo == (unsigned long)(index))
00179 indexTo = (unsigned long)(index + 1);
00180
00181 double valL = 0, valR = 0;
00182 #if RUBBERBAND
00183 if ( rubberband ) {
00184 valL = magnitudes[ i ];
00185 valR = magnitudes[ i + size.width() ];
00186 if (valL < 0.) {
00187 valL += falloff;
00188 if ( valL > 0. )
00189 valL = 0.;
00190 }
00191 else
00192 {
00193 valL -= falloff;
00194 if ( valL < 0. )
00195 valL = 0.;
00196 }
00197 if (valR < 0.)
00198 {
00199 valR += falloff;
00200 if ( valR > 0. )
00201 valR = 0.;
00202 }
00203 else
00204 {
00205 valR -= falloff;
00206 if ( valR < 0. )
00207 valR = 0.;
00208 }
00209 }
00210 #endif
00211 for (unsigned long s = (unsigned long)index; s < indexTo && s < node->length; s++)
00212 {
00213 double tmpL = ( ( node->left ?
00214 double( node->left[s] ) : 0.) *
00215 double( size.height() / 4 ) ) / 32768.;
00216 double tmpR = ( ( node->right ?
00217 double( node->right[s]) : 0.) *
00218 double( size.height() / 4 ) ) / 32768.;
00219 if (tmpL > 0)
00220 valL = (tmpL > valL) ? tmpL : valL;
00221 else
00222 valL = (tmpL < valL) ? tmpL : valL;
00223 if (tmpR > 0)
00224 valR = (tmpR > valR) ? tmpR : valR;
00225 else
00226 valR = (tmpR < valR) ? tmpR : valR;
00227 }
00228
00229 if (valL != 0. || valR != 0.)
00230 allZero = false;
00231
00232 magnitudes[ i ] = valL;
00233 magnitudes[ i + size.width() ] = valR;
00234
00235 index = index + step;
00236 }
00237 #if RUBBERBAND
00238 }
00239 else if (rubberband)
00240 {
00241 for ( int i = 0; i < size.width(); i++)
00242 {
00243 double valL = magnitudes[ i ];
00244 if (valL < 0) {
00245 valL += 2;
00246 if (valL > 0.)
00247 valL = 0.;
00248 } else {
00249 valL -= 2;
00250 if (valL < 0.)
00251 valL = 0.;
00252 }
00253
00254 double valR = magnitudes[ i + size.width() ];
00255 if (valR < 0.) {
00256 valR += falloff;
00257 if (valR > 0.)
00258 valR = 0.;
00259 }
00260 else
00261 {
00262 valR -= falloff;
00263 if (valR < 0.)
00264 valR = 0.;
00265 }
00266
00267 if (valL != 0. || valR != 0.)
00268 allZero = false;
00269
00270 magnitudes[ i ] = valL;
00271 magnitudes[ i + size.width() ] = valR;
00272 }
00273 #endif
00274 }
00275 else
00276 {
00277 for ( int i = 0; (unsigned) i < magnitudes.size(); i++ )
00278 magnitudes[ i ] = 0.;
00279 }
00280
00281 return allZero;
00282 }
00283
00284 bool StereoScope::draw( QPainter *p, const QColor &back )
00285 {
00286 p->fillRect(0, 0, size.width(), size.height(), back);
00287 for ( int i = 1; i < size.width(); i++ )
00288 {
00289 #if TWOCOLOUR
00290 double r, g, b, per;
00291
00292
00293 per = double( magnitudes[ i ] * 2 ) /
00294 double( size.height() / 4 );
00295 if (per < 0.0)
00296 per = -per;
00297 if (per > 1.0)
00298 per = 1.0;
00299 else if (per < 0.0)
00300 per = 0.0;
00301
00302 r = startColor.red() + (targetColor.red() -
00303 startColor.red()) * (per * per);
00304 g = startColor.green() + (targetColor.green() -
00305 startColor.green()) * (per * per);
00306 b = startColor.blue() + (targetColor.blue() -
00307 startColor.blue()) * (per * per);
00308
00309 if (r > 255.0)
00310 r = 255.0;
00311 else if (r < 0.0)
00312 r = 0;
00313
00314 if (g > 255.0)
00315 g = 255.0;
00316 else if (g < 0.0)
00317 g = 0;
00318
00319 if (b > 255.0)
00320 b = 255.0;
00321 else if (b < 0.0)
00322 b = 0;
00323
00324 p->setPen( QColor( int(r), int(g), int(b) ) );
00325 #else
00326 p->setPen(Qt::red);
00327 #endif
00328 p->drawLine( i - 1, (int)((size.height() / 4) + magnitudes[i - 1]),
00329 i, (int)((size.height() / 4) + magnitudes[i]));
00330
00331 #if TWOCOLOUR
00332
00333 per = double( magnitudes[ i + size.width() ] * 2 ) /
00334 double( size.height() / 4 );
00335 if (per < 0.0)
00336 per = -per;
00337 if (per > 1.0)
00338 per = 1.0;
00339 else if (per < 0.0)
00340 per = 0.0;
00341
00342 r = startColor.red() + (targetColor.red() -
00343 startColor.red()) * (per * per);
00344 g = startColor.green() + (targetColor.green() -
00345 startColor.green()) * (per * per);
00346 b = startColor.blue() + (targetColor.blue() -
00347 startColor.blue()) * (per * per);
00348
00349 if (r > 255.0)
00350 r = 255.0;
00351 else if (r < 0.0)
00352 r = 0;
00353
00354 if (g > 255.0)
00355 g = 255.0;
00356 else if (g < 0.0)
00357 g = 0;
00358
00359 if (b > 255.0)
00360 b = 255.0;
00361 else if (b < 0.0)
00362 b = 0;
00363
00364 p->setPen( QColor( int(r), int(g), int(b) ) );
00365 #else
00366 p->setPen(Qt::red);
00367 #endif
00368 p->drawLine( i - 1, (int)((size.height() * 3 / 4) +
00369 magnitudes[i + size.width() - 1]),
00370 i, (int)((size.height() * 3 / 4) +
00371 magnitudes[i + size.width()]));
00372 }
00373
00374 return true;
00375 }
00376
00378
00379
00380 MonoScope::MonoScope()
00381 {
00382 }
00383
00384 MonoScope::~MonoScope()
00385 {
00386 }
00387
00388 bool MonoScope::process( VisualNode *node )
00389 {
00390 bool allZero = true;
00391
00392 if (node)
00393 {
00394 double index = 0;
00395 double const step = (double)SAMPLES_DEFAULT_SIZE / size.width();
00396 for (int i = 0; i < size.width(); i++)
00397 {
00398 unsigned long indexTo = (unsigned long)(index + step);
00399 if (indexTo == (unsigned long)index)
00400 indexTo = (unsigned long)(index + 1);
00401
00402 double val = 0;
00403 #if RUBBERBAND
00404 if ( rubberband )
00405 {
00406 val = magnitudes[ i ];
00407 if (val < 0.)
00408 {
00409 val += falloff;
00410 if ( val > 0. )
00411 {
00412 val = 0.;
00413 }
00414 }
00415 else
00416 {
00417 val -= falloff;
00418 if ( val < 0. )
00419 {
00420 val = 0.;
00421 }
00422 }
00423 }
00424 #endif
00425 for (unsigned long s = (unsigned long)index; s < indexTo && s < node->length; s++)
00426 {
00427 double tmp = ( double( node->left[s] ) +
00428 (node->right ? double( node->right[s] ) : 0) *
00429 double( size.height() / 2 ) ) / 65536.;
00430 if (tmp > 0)
00431 {
00432 val = (tmp > val) ? tmp : val;
00433 }
00434 else
00435 {
00436 val = (tmp < val) ? tmp : val;
00437 }
00438 }
00439
00440 if ( val != 0. )
00441 {
00442 allZero = false;
00443 }
00444 magnitudes[ i ] = val;
00445 index = index + step;
00446 }
00447 }
00448 #if RUBBERBAND
00449 else if (rubberband)
00450 {
00451 for (int i = 0; i < size.width(); i++) {
00452 double val = magnitudes[ i ];
00453 if (val < 0) {
00454 val += 2;
00455 if (val > 0.)
00456 val = 0.;
00457 } else {
00458 val -= 2;
00459 if (val < 0.)
00460 val = 0.;
00461 }
00462
00463 if ( val != 0. )
00464 allZero = false;
00465 magnitudes[ i ] = val;
00466 }
00467 }
00468 #endif
00469 else
00470 {
00471 for (int i = 0; i < size.width(); i++ )
00472 magnitudes[ i ] = 0.;
00473 }
00474
00475 return allZero;
00476 }
00477
00478 bool MonoScope::draw( QPainter *p, const QColor &back )
00479 {
00480 p->fillRect( 0, 0, size.width(), size.height(), back );
00481 for ( int i = 1; i < size.width(); i++ ) {
00482 #if TWOCOLOUR
00483 double r, g, b, per;
00484
00485 per = double( magnitudes[ i ] ) /
00486 double( size.height() / 4 );
00487 if (per < 0.0)
00488 per = -per;
00489 if (per > 1.0)
00490 per = 1.0;
00491 else if (per < 0.0)
00492 per = 0.0;
00493
00494 r = startColor.red() + (targetColor.red() -
00495 startColor.red()) * (per * per);
00496 g = startColor.green() + (targetColor.green() -
00497 startColor.green()) * (per * per);
00498 b = startColor.blue() + (targetColor.blue() -
00499 startColor.blue()) * (per * per);
00500
00501 if (r > 255.0)
00502 r = 255.0;
00503 else if (r < 0.0)
00504 r = 0;
00505
00506 if (g > 255.0)
00507 g = 255.0;
00508 else if (g < 0.0)
00509 g = 0;
00510
00511 if (b > 255.0)
00512 b = 255.0;
00513 else if (b < 0.0)
00514 b = 0;
00515
00516 p->setPen(QColor(int(r), int(g), int(b)));
00517 #else
00518 p->setPen(Qt::red);
00519 #endif
00520 p->drawLine( i - 1, (int)(size.height() / 2 + magnitudes[ i - 1 ]),
00521 i, (int)(size.height() / 2 + magnitudes[ i ] ));
00522 }
00523
00524 return true;
00525 }
00526
00528
00529
00530 static class StereoScopeFactory : public VisFactory
00531 {
00532 public:
00533 const QString &name(void) const
00534 {
00535 static QString name = QCoreApplication::translate("Visualizers",
00536 "StereoScope");
00537 return name;
00538 }
00539
00540 uint plugins(QStringList *list) const
00541 {
00542 *list << name();
00543 return 1;
00544 }
00545
00546 VisualBase *create(MainVisual *parent, const QString &pluginName) const
00547 {
00548 (void)parent;
00549 (void)pluginName;
00550 return new StereoScope();
00551 }
00552 }StereoScopeFactory;
00553
00554
00556
00557
00558 static class MonoScopeFactory : public VisFactory
00559 {
00560 public:
00561 const QString &name(void) const
00562 {
00563 static QString name = QCoreApplication::translate("Visualizers",
00564 "MonoScope");
00565 return name;
00566 }
00567
00568 uint plugins(QStringList *list) const
00569 {
00570 *list << name();
00571 return 1;
00572 }
00573
00574 VisualBase *create(MainVisual *parent, const QString &pluginName) const
00575 {
00576 (void)parent;
00577 (void)pluginName;
00578 return new MonoScope();
00579 }
00580 }MonoScopeFactory;
00581
00583
00584
00585
00586
00587 #if FFTW3_SUPPORT
00588 Spectrum::Spectrum()
00589 : lin(NULL), rin(NULL), lout(NULL), rout(NULL)
00590 {
00591
00592
00593 analyzerBarWidth = 6;
00594 scaleFactor = 2.0;
00595 falloff = 10.0;
00596 m_fps = 15;
00597
00598 lin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
00599 memset(lin, 0, sizeof(myth_fftw_float)*FFTW_N);
00600
00601 rin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
00602 memset(rin, 0, sizeof(myth_fftw_float)*FFTW_N);
00603
00604 lout = (myth_fftw_complex*) av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
00605 memset(lout, 0, sizeof(myth_fftw_complex)*(FFTW_N/2+1));
00606
00607 rout = (myth_fftw_complex*) av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
00608 memset(rout, 0, sizeof(myth_fftw_complex)*(FFTW_N/2+1));
00609
00610 lplan = fftw_plan_dft_r2c_1d(FFTW_N, lin, (myth_fftw_complex_cast*)lout, FFTW_MEASURE);
00611 rplan = fftw_plan_dft_r2c_1d(FFTW_N, rin, (myth_fftw_complex_cast*)rout, FFTW_MEASURE);
00612
00613 startColor = QColor(0,0,255);
00614 targetColor = QColor(255,0,0);
00615 }
00616
00617 Spectrum::~Spectrum()
00618 {
00619 if (lin)
00620 av_free(lin);
00621 if (rin)
00622 av_free(rin);
00623 if (lout)
00624 av_free(lout);
00625 if (rout)
00626 av_free(rout);
00627
00628 fftw_destroy_plan(lplan);
00629 fftw_destroy_plan(rplan);
00630 }
00631
00632 void Spectrum::resize(const QSize &newsize)
00633 {
00634
00635
00636
00637
00638
00639
00640 size = newsize;
00641
00642 analyzerBarWidth = size.width() / 64;
00643
00644 if (analyzerBarWidth < 6)
00645 analyzerBarWidth = 6;
00646
00647 scale.setMax(192, size.width() / analyzerBarWidth);
00648
00649 rects.resize( scale.range() );
00650 unsigned int i = 0;
00651 int w = 0;
00652 for (; i < (uint)rects.size(); i++, w += analyzerBarWidth)
00653 {
00654 rects[i].setRect(w, size.height() / 2, analyzerBarWidth - 1, 1);
00655 }
00656
00657 unsigned int os = magnitudes.size();
00658 magnitudes.resize( scale.range() * 2 );
00659 for (; os < (uint)magnitudes.size(); os++)
00660 {
00661 magnitudes[os] = 0.0;
00662 }
00663
00664 scaleFactor = double( size.height() / 2 ) / log( (double)(FFTW_N) );
00665 }
00666
00667 template<typename T> T sq(T a) { return a*a; };
00668
00669 bool Spectrum::process(VisualNode *node)
00670 {
00671
00672
00673
00674 bool allZero = true;
00675
00676 uint i;
00677 long w = 0, index;
00678 QRect *rectsp = rects.data();
00679 double *magnitudesp = magnitudes.data();
00680 double magL, magR, tmp;
00681
00682 if (node)
00683 {
00684 i = node->length;
00685 if (i > FFTW_N)
00686 i = FFTW_N;
00687 fast_real_set_from_short(lin, node->left, i);
00688 if (node->right)
00689 fast_real_set_from_short(rin, node->right, i);
00690 }
00691 else
00692 i = 0;
00693
00694 fast_reals_set(lin + i, rin + i, 0, FFTW_N - i);
00695
00696 fftw_execute(lplan);
00697 fftw_execute(rplan);
00698
00699 index = 1;
00700
00701 for (i = 0; (int)i < rects.size(); i++, w += analyzerBarWidth)
00702 {
00703 magL = (log(sq(real(lout[index])) + sq(real(lout[FFTW_N - index]))) - 22.0) *
00704 scaleFactor;
00705 magR = (log(sq(real(rout[index])) + sq(real(rout[FFTW_N - index]))) - 22.0) *
00706 scaleFactor;
00707
00708 if (magL > size.height() / 2)
00709 {
00710 magL = size.height() / 2;
00711 }
00712 if (magL < magnitudesp[i])
00713 {
00714 tmp = magnitudesp[i] - falloff;
00715 if ( tmp < magL )
00716 {
00717 tmp = magL;
00718 }
00719 magL = tmp;
00720 }
00721 if (magL < 1.)
00722 {
00723 magL = 1.;
00724 }
00725
00726 if (magR > size.height() / 2)
00727 {
00728 magR = size.height() / 2;
00729 }
00730 if (magR < magnitudesp[i + scale.range()])
00731 {
00732 tmp = magnitudesp[i + scale.range()] - falloff;
00733 if ( tmp < magR )
00734 {
00735 tmp = magR;
00736 }
00737 magR = tmp;
00738 }
00739 if (magR < 1.)
00740 {
00741 magR = 1.;
00742 }
00743
00744 if (magR != 1 || magL != 1)
00745 {
00746 allZero = false;
00747 }
00748
00749 magnitudesp[i] = magL;
00750 magnitudesp[i + scale.range()] = magR;
00751 rectsp[i].setTop( size.height() / 2 - int( magL ) );
00752 rectsp[i].setBottom( size.height() / 2 + int( magR ) );
00753
00754 index = scale[i];
00755 }
00756
00757 return false;
00758 }
00759
00760 double Spectrum::clamp(double cur, double max, double min)
00761 {
00762 if (cur > max)
00763 cur = max;
00764 if (cur < min)
00765 cur = min;
00766 return cur;
00767 }
00768
00769 bool Spectrum::draw(QPainter *p, const QColor &back)
00770 {
00771
00772
00773
00774
00775
00776
00777 QRect *rectsp = rects.data();
00778 double r, g, b, per;
00779
00780 p->fillRect(0, 0, size.width(), size.height(), back);
00781 for (uint i = 0; i < (uint)rects.size(); i++)
00782 {
00783 per = double( rectsp[i].height() - 2 ) / double( size.height() );
00784
00785 per = clamp(per, 1.0, 0.0);
00786
00787 r = startColor.red() +
00788 (targetColor.red() - startColor.red()) * (per * per);
00789 g = startColor.green() +
00790 (targetColor.green() - startColor.green()) * (per * per);
00791 b = startColor.blue() +
00792 (targetColor.blue() - startColor.blue()) * (per * per);
00793
00794 r = clamp(r, 255.0, 0.0);
00795 g = clamp(g, 255.0, 0.0);
00796 b = clamp(b, 255.0, 0.0);
00797
00798 if(rectsp[i].height() > 4)
00799 p->fillRect(rectsp[i], QColor(int(r), int(g), int(b)));
00800 }
00801
00802 return true;
00803 }
00804
00805 static class SpectrumFactory : public VisFactory
00806 {
00807 public:
00808 const QString &name(void) const
00809 {
00810 static QString name = QCoreApplication::translate("Visualizers",
00811 "Spectrum");
00812 return name;
00813 }
00814
00815 uint plugins(QStringList *list) const
00816 {
00817 *list << name();
00818 return 1;
00819 }
00820
00821 VisualBase *create(MainVisual *parent, const QString &pluginName) const
00822 {
00823 (void)parent;
00824 (void)pluginName;
00825 return new Spectrum();
00826 }
00827 }SpectrumFactory;
00828
00830
00831
00832
00833
00834 Squares::Squares() :
00835 size(0,0), pParent(NULL), fake_height(0), number_of_squares(16)
00836 {
00837 fake_height = number_of_squares * analyzerBarWidth;
00838 }
00839
00840 Squares::~Squares()
00841 {
00842 }
00843
00844 void Squares::resize (const QSize &newsize) {
00845
00846 Spectrum::resize (QSize (fake_height, fake_height));
00847
00848 size = newsize;
00849 }
00850
00851 void Squares::drawRect(QPainter *p, QRect *rect, int i, int c, int w, int h)
00852 {
00853 double r, g, b, per;
00854 int correction = (size.width() % rects.size ()) / 2;
00855 int x = ((i / 2) * w) + correction;
00856 int y;
00857
00858 if (i % 2 == 0)
00859 {
00860 y = c - h;
00861 per = double(fake_height - rect->top()) / double(fake_height);
00862 }
00863 else
00864 {
00865 y = c;
00866 per = double(rect->bottom()) / double(fake_height);
00867 }
00868
00869 per = clamp(per, 1.0, 0.0);
00870
00871 r = startColor.red() +
00872 (targetColor.red() - startColor.red()) * (per * per);
00873 g = startColor.green() +
00874 (targetColor.green() - startColor.green()) * (per * per);
00875 b = startColor.blue() +
00876 (targetColor.blue() - startColor.blue()) * (per * per);
00877
00878 r = clamp(r, 255.0, 0.0);
00879 g = clamp(g, 255.0, 0.0);
00880 b = clamp(b, 255.0, 0.0);
00881
00882 p->fillRect (x, y, w, h, QColor (int(r), int(g), int(b)));
00883 }
00884
00885 bool Squares::draw(QPainter *p, const QColor &back)
00886 {
00887 p->fillRect (0, 0, size.width (), size.height (), back);
00888 int w = size.width () / (rects.size () / 2);
00889 int h = w;
00890 int center = size.height () / 2;
00891
00892 QRect *rectsp = rects.data();
00893 for (uint i = 0; i < (uint)rects.size(); i++)
00894 drawRect(p, &(rectsp[i]), i, center, w, h);
00895
00896 return true;
00897 }
00898
00899 static class SquaresFactory : public VisFactory
00900 {
00901 public:
00902 const QString &name(void) const
00903 {
00904 static QString name = QCoreApplication::translate("Visualizers",
00905 "Squares");
00906 return name;
00907 }
00908
00909 uint plugins(QStringList *list) const
00910 {
00911 *list << name();
00912 return 1;
00913 }
00914
00915 VisualBase *create(MainVisual *parent, const QString &pluginName) const
00916 {
00917 (void)parent;
00918 (void)pluginName;
00919 return new Squares();
00920 }
00921 }SquaresFactory;
00922
00923 #endif // FFTW3_SUPPORT
00924
00925 Piano::Piano()
00926 : piano_data(NULL), audio_data(NULL)
00927 {
00928
00929
00930
00931 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Initialised"));
00932
00933 piano_data = (piano_key_data *) malloc(sizeof(piano_key_data) * PIANO_N);
00934 audio_data = (piano_audio *) malloc(sizeof(piano_audio) * PIANO_AUDIO_SIZE);
00935
00936 double sample_rate = 44100.0;
00937
00938 m_fps = 20;
00939
00940 double concert_A = 440.0;
00941 double semi_tone = pow(2.0, 1.0/12.0);
00942
00943
00944 double bottom_A = concert_A / 2.0 / 2.0 / 2.0 / 2.0;
00945
00946 unsigned int key;
00947 double current_freq = bottom_A, samples_required;
00948 for (key = 0; key < PIANO_N; key++)
00949 {
00950
00951 piano_data[key].coeff = (goertzel_data)(2.0 * cos(2.0 * M_PI * current_freq / sample_rate));
00952
00953 samples_required = sample_rate/current_freq * 20.0;
00954 if (samples_required > sample_rate/4.0)
00955 {
00956
00957 samples_required = sample_rate/4.0;
00958 }
00959 if (samples_required < sample_rate/(double)m_fps * 0.75)
00960 {
00961 samples_required = sample_rate/(double)m_fps * 0.75;
00962 }
00963 piano_data[key].samples_process_before_display_update = (int)samples_required;
00964 piano_data[key].is_black_note = false;
00965
00966 current_freq *= semi_tone;
00967 }
00968
00969 zero_analysis();
00970
00971 whiteStartColor = QColor(245,245,245);
00972 whiteTargetColor = Qt::red;
00973
00974 blackStartColor = QColor(10,10,10);
00975 blackTargetColor = Qt::red;
00976 }
00977
00978 Piano::~Piano()
00979 {
00980 if (piano_data)
00981 free(piano_data);
00982 if (audio_data)
00983 free(audio_data);
00984 }
00985
00986 void Piano::zero_analysis(void)
00987 {
00988 unsigned int key;
00989 for (key = 0; key < PIANO_N; key++)
00990 {
00991
00992 piano_data[key].q2 = (goertzel_data)0.0f;
00993 piano_data[key].q1 = (goertzel_data)0.0f;
00994 piano_data[key].magnitude = (goertzel_data)0.0f;
00995 piano_data[key].max_magnitude_seen =
00996 (goertzel_data)(PIANO_RMS_NEGLIGIBLE*PIANO_RMS_NEGLIGIBLE);
00997
00998 piano_data[key].samples_processed = 0;
00999 }
01000 offset_processed = 0;
01001
01002 return;
01003 }
01004
01005 void Piano::resize(const QSize &newsize)
01006 {
01007
01008
01009
01010
01011
01012
01013 size = newsize;
01014
01015 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Resized"));
01016
01017 zero_analysis();
01018
01019
01020 double key_unit_size = (double)size.width() / 54.0;
01021 if (key_unit_size < 10.0)
01022 key_unit_size = 10.0;
01023
01024 double white_width_pct = .8;
01025 double black_width_pct = .6;
01026 double black_offset_pct = .05;
01027
01028 double white_height_pct = 6;
01029 double black_height_pct = 4;
01030
01031
01032
01033 double left = (double)size.width() / 2.0 - (4.0*7.0 + 3.5) * key_unit_size;
01034 double top_of_keys = (double)size.height() / 2.0 - key_unit_size * white_height_pct / 2.0;
01035
01036 double width, height, center, offset;
01037
01038 rects.resize(PIANO_N);
01039
01040 unsigned int key;
01041 int note;
01042 bool is_black = false;
01043 for (key = 0; key < PIANO_N; key++)
01044 {
01045 note = ((int)key - 3 + 12) % 12;
01046 if (note == 0)
01047 {
01048 left += key_unit_size*7.0;
01049 }
01050
01051 center = 0.0;
01052 offset = 0.0;
01053 is_black = false;
01054
01055 switch (note)
01056 {
01057 case 0: center = 0.5; break;
01058 case 1: center = 1.0; is_black = true; offset = -1; break;
01059 case 2: center = 1.5; break;
01060 case 3: center = 2.0; is_black = true; offset = +1; break;
01061 case 4: center = 2.5; break;
01062 case 5: center = 3.5; break;
01063 case 6: center = 4.0; is_black = true; offset = -2; break;
01064 case 7: center = 4.5; break;
01065 case 8: center = 5.0; is_black = true; offset = 0; break;
01066 case 9: center = 5.5; break;
01067 case 10: center = 6.0; is_black = true; offset = 2; break;
01068 case 11: center = 6.5; break;
01069 }
01070 piano_data[key].is_black_note = is_black;
01071
01072 width = (is_black ? black_width_pct:white_width_pct) * key_unit_size;
01073 height = (is_black? black_height_pct:white_height_pct) * key_unit_size;
01074
01075 rects[key].setRect(
01076 left + center * key_unit_size
01077 - width / 2.0
01078 + (is_black ? (offset * black_offset_pct * key_unit_size):0.0),
01079 top_of_keys,
01080 width,
01081 height
01082 );
01083 }
01084
01085 magnitude.resize(PIANO_N);
01086 for (key = 0; key < (uint)magnitude.size(); key++)
01087 {
01088 magnitude[key] = 0.0;
01089 }
01090
01091 return;
01092 }
01093
01094 unsigned long Piano::getDesiredSamples(void)
01095 {
01096
01097
01098
01099
01100
01101 return (unsigned long) PIANO_AUDIO_SIZE;
01102 }
01103
01104 bool Piano::processUndisplayed(VisualNode *node)
01105 {
01106
01107 return process_all_types(node, false);
01108 }
01109
01110 bool Piano::process(VisualNode *node)
01111 {
01112
01113
01114 process_all_types(node, true);
01115 return false;
01116 }
01117
01118 bool Piano::process_all_types(VisualNode *node, bool this_will_be_displayed)
01119 {
01120 (void) this_will_be_displayed;
01121
01122
01123
01124 bool allZero = true;
01125
01126 uint i, n, key;
01127
01128 goertzel_data q0, q1, q2, coeff;
01129 goertzel_data magnitude2, magnitude_av;
01130 piano_audio short_to_bounded = 32768.0f;
01131 int n_samples;
01132
01133 if (node)
01134 {
01135
01136 if (node->offset + 10000 < offset_processed)
01137 {
01138 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Node offset=%1 too far backwards : NEW SONG").arg(node->offset));
01139 zero_analysis();
01140 }
01141
01142
01143 if (node->offset <= offset_processed)
01144 {
01145 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Already seen node offset=%1, returning without processing").arg(node->offset));
01146 return allZero;
01147 }
01148 }
01149
01150 if (node)
01151 {
01152
01153 n = node->length;
01154
01155 if (node->right)
01156 {
01157 for (i = 0; i < n; i++)
01158 {
01159 audio_data[i] = (piano_audio)(((piano_audio)node->left[i] + (piano_audio)node->right[i]) / 2.0 / short_to_bounded);
01160 }
01161 }
01162 else
01163 {
01164 for (i = 0; i < n; i++)
01165 {
01166 audio_data[i] = (piano_audio)node->left[i] / short_to_bounded;
01167 }
01168 }
01169 }
01170 else
01171 {
01172 LOG(VB_GENERAL, LOG_DEBUG, QString("Hit an empty node, and returning empty-handed"));
01173 return allZero;
01174 }
01175
01176 for (key = 0; key < PIANO_N; key++)
01177 {
01178 coeff = piano_data[key].coeff;
01179
01180 q2 = piano_data[key].q2;
01181 q1 = piano_data[key].q1;
01182
01183 for (i = 0; i < n; i++)
01184 {
01185 q0 = coeff * q1 - q2 + audio_data[i];
01186 q2 = q1;
01187 q1 = q0;
01188 }
01189 piano_data[key].q2 = q2;
01190 piano_data[key].q1 = q1;
01191
01192 piano_data[key].samples_processed += n;
01193
01194 n_samples = piano_data[key].samples_processed;
01195
01196
01197 if (n_samples > piano_data[key].samples_process_before_display_update)
01198 {
01199 magnitude2 = q1*q1 + q2*q2 - q1*q2*coeff;
01200
01201 if (false)
01202 {
01203 magnitude_av = sqrt(magnitude2)/(goertzel_data)n_samples;
01204 }
01205 if (true)
01206 {
01207 magnitude_av = magnitude2/(goertzel_data)n_samples/(goertzel_data)n_samples;
01208 }
01209
01210 if (false)
01211 {
01212 if(magnitude_av > 0.0)
01213 {
01214 magnitude_av = log(magnitude_av);
01215 }
01216 else
01217 {
01218 magnitude_av = PIANO_MIN_VOL;
01219 }
01220 magnitude_av -= PIANO_MIN_VOL;
01221
01222 if (magnitude_av < 0.0)
01223 {
01224 magnitude_av = 0.0;
01225 }
01226 }
01227
01228 if (magnitude_av > (goertzel_data)0.01)
01229 {
01230 allZero = false;
01231 }
01232
01233 piano_data[key].magnitude = magnitude_av;
01234 if ( piano_data[key].max_magnitude_seen < magnitude_av)
01235 {
01236 piano_data[key].max_magnitude_seen = magnitude_av;
01237 }
01238 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Updated Key %1 from %2 samples, magnitude=%3")
01239 .arg(key).arg(n_samples).arg(magnitude_av));
01240
01241 piano_data[key].samples_processed = 0;
01242 piano_data[key].q1 = (goertzel_data)0.0;
01243 piano_data[key].q2 = (goertzel_data)0.0;
01244 }
01245 }
01246
01247
01248 offset_processed = node->offset;
01249 return allZero;
01250 }
01251
01252 double Piano::clamp(double cur, double max, double min)
01253 {
01254 if (cur > max)
01255 cur = max;
01256 if (cur < min)
01257 cur = min;
01258 return cur;
01259 }
01260
01261 bool Piano::draw(QPainter *p, const QColor &back)
01262 {
01263
01264
01265
01266
01267
01268
01269 QRect *rectsp = &rects[0];
01270 double *magnitudep = &magnitude[0];
01271
01272 unsigned int key, n = PIANO_N;
01273 double r, g, b, per;
01274
01275 p->fillRect(0, 0, size.width(), size.height(), back);
01276
01277
01278 if(n > (uint)rects.size())
01279 n = (uint)rects.size();
01280
01281
01282 double mag = PIANO_RMS_NEGLIGIBLE;
01283 for (key = 0; key < n; key++)
01284 {
01285 if (piano_data[key].max_magnitude_seen < mag)
01286 {
01287
01288 piano_data[key].max_magnitude_seen = mag;
01289 }
01290 else
01291 {
01292
01293 mag = piano_data[key].max_magnitude_seen;
01294 }
01295 mag *= PIANO_SPECTRUM_SMOOTHING;
01296 }
01297
01298
01299 mag = PIANO_RMS_NEGLIGIBLE;
01300 for (int key_i = n - 1; key_i >= 0; key_i--)
01301 {
01302 key = key_i;
01303 if (piano_data[key].max_magnitude_seen < mag)
01304 {
01305
01306 piano_data[key].max_magnitude_seen = mag;
01307 }
01308 else
01309 {
01310
01311 mag = piano_data[key].max_magnitude_seen;
01312 }
01313 mag *= PIANO_SPECTRUM_SMOOTHING;
01314 }
01315
01316
01317
01318 double magnitude_max = PIANO_RMS_NEGLIGIBLE;
01319 for (key = 0; key < n; key++)
01320 {
01321 mag = piano_data[key].magnitude / piano_data[key].max_magnitude_seen;
01322 if (magnitude_max < mag)
01323 magnitude_max = mag;
01324
01325 magnitudep[key] = mag;
01326 }
01327
01328
01329 for (key = 0; key < n; key++)
01330 {
01331 if (piano_data[key].is_black_note)
01332 continue;
01333
01334 per = magnitudep[key] / magnitude_max;
01335 per = clamp(per, 1.0, 0.0);
01336
01337 if (per < PIANO_KEYPRESS_TOO_LIGHT)
01338 per = 0.0;
01339 LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Display key %1, magnitude=%2, seen=%3")
01340 .arg(key).arg(per*100.0).arg(piano_data[key].max_magnitude_seen));
01341
01342 r = whiteStartColor.red() + (whiteTargetColor.red() - whiteStartColor.red()) * per;
01343 g = whiteStartColor.green() + (whiteTargetColor.green() - whiteStartColor.green()) * per;
01344 b = whiteStartColor.blue() + (whiteTargetColor.blue() - whiteStartColor.blue()) * per;
01345
01346 p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
01347 }
01348
01349
01350 for (key = 0; key < n; key++)
01351 {
01352 if (!piano_data[key].is_black_note)
01353 continue;
01354
01355 per = magnitudep[key]/magnitude_max;
01356 per = clamp(per, 1.0, 0.0);
01357
01358 if (per < PIANO_KEYPRESS_TOO_LIGHT)
01359 per = 0.0;
01360
01361 r = blackStartColor.red() + (blackTargetColor.red() - blackStartColor.red()) * per;
01362 g = blackStartColor.green() + (blackTargetColor.green() - blackStartColor.green()) * per;
01363 b = blackStartColor.blue() + (blackTargetColor.blue() - blackStartColor.blue()) * per;
01364
01365 p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
01366 }
01367
01368 return true;
01369 }
01370
01371 static class PianoFactory : public VisFactory
01372 {
01373 public:
01374 const QString &name(void) const
01375 {
01376 static QString name = QCoreApplication::translate("Visualizers",
01377 "Piano");
01378 return name;
01379 }
01380
01381 uint plugins(QStringList *list) const
01382 {
01383 *list << name();
01384 return 1;
01385 }
01386
01387 VisualBase *create(MainVisual *parent, const QString &pluginName) const
01388 {
01389 (void)parent;
01390 (void)pluginName;
01391 return new Piano();
01392 }
01393 }PianoFactory;
01394
01395 AlbumArt::AlbumArt(void) :
01396 m_currentMetadata(NULL),
01397 m_lastCycle(QDateTime::currentDateTime())
01398 {
01399 findFrontCover();
01400 m_fps = 1;
01401 }
01402
01403 void AlbumArt::findFrontCover(void)
01404 {
01405 if (!gPlayer->getCurrentMetadata())
01406 return;
01407
01408
01409 AlbumArtImages *albumArt = gPlayer->getCurrentMetadata()->getAlbumArtImages();
01410 if (albumArt->getImage(IT_FRONTCOVER))
01411 m_currImageType = IT_FRONTCOVER;
01412 else
01413 {
01414
01415 if (albumArt->getImageCount() > 0)
01416 m_currImageType = albumArt->getImageAt(0)->imageType;
01417 else
01418 m_currImageType = IT_UNKNOWN;
01419 }
01420 }
01421
01422 bool AlbumArt::cycleImage(void)
01423 {
01424 if (!gPlayer->getCurrentMetadata())
01425 return false;
01426
01427 AlbumArtImages *albumArt = gPlayer->getCurrentMetadata()->getAlbumArtImages();
01428 int newType = m_currImageType;
01429
01430 if (albumArt->getImageCount() > 0)
01431 {
01432 do
01433 {
01434 newType++;
01435 if (newType == IT_LAST)
01436 newType = IT_UNKNOWN;
01437 } while (!albumArt->getImage((ImageType) newType));
01438 }
01439
01440 if (newType != m_currImageType)
01441 {
01442 m_currImageType = (ImageType) newType;
01443 m_lastCycle = QDateTime::currentDateTime();
01444 return true;
01445 }
01446
01447 return false;
01448 }
01449
01450 AlbumArt::~AlbumArt()
01451 {
01452 }
01453
01454 void AlbumArt::resize(const QSize &newsize)
01455 {
01456 m_size = newsize;
01457 }
01458
01459 bool AlbumArt::process(VisualNode *node)
01460 {
01461 (void) node;
01462 return false;
01463 }
01464
01465 void AlbumArt::handleKeyPress(const QString &action)
01466 {
01467 if (action == "SELECT")
01468 {
01469 if (gPlayer->getCurrentMetadata())
01470 {
01471 AlbumArtImages albumArt(gPlayer->getCurrentMetadata());
01472 int newType = m_currImageType;
01473
01474 if (albumArt.getImageCount() > 0)
01475 {
01476 newType++;
01477
01478 while (!albumArt.getImage((ImageType) newType))
01479 {
01480 newType++;
01481 if (newType == IT_LAST)
01482 newType = IT_UNKNOWN;
01483 }
01484 }
01485
01486 if (newType != m_currImageType)
01487 {
01488 m_currImageType = (ImageType) newType;
01489
01490 m_cursize = QSize(0, 0);
01491 }
01492 }
01493 }
01494 }
01495
01497 #define ALBUMARTCYCLETIME 10
01498
01499 bool AlbumArt::needsUpdate()
01500 {
01501
01502 if (gPlayer->getCurrentMetadata() && m_currentMetadata != gPlayer->getCurrentMetadata())
01503 {
01504 m_currentMetadata = gPlayer->getCurrentMetadata();
01505 findFrontCover();
01506 return true;
01507 }
01508
01509
01510 if (m_lastCycle.addSecs(ALBUMARTCYCLETIME) < QDateTime::currentDateTime())
01511 {
01512 if (cycleImage())
01513 return true;
01514 }
01515
01516 return false;
01517 }
01518
01519 bool AlbumArt::draw(QPainter *p, const QColor &back)
01520 {
01521 if (needsUpdate())
01522 {
01523 QImage art;
01524 QString imageFilename = gPlayer->getCurrentMetadata()->getAlbumArtFile(m_currImageType);
01525 if (!imageFilename.isEmpty())
01526 art.load(imageFilename);
01527
01528 if (art.isNull())
01529 {
01530 m_cursize = m_size;
01531 m_image = QImage();
01532 }
01533 else
01534 {
01535 m_image = art.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
01536 }
01537 }
01538
01539 if (m_image.isNull())
01540 {
01541 drawWarning(p, back, m_size, QObject::tr("?"), 100);
01542 return true;
01543 }
01544
01545
01546 p->fillRect(0, 0, m_size.width(), m_size.height(), back);
01547 p->drawImage((m_size.width() - m_image.width()) / 2,
01548 (m_size.height() - m_image.height()) / 2,
01549 m_image);
01550
01551
01552 m_cursize = m_size;
01553
01554 return true;
01555 }
01556
01557 static class AlbumArtFactory : public VisFactory
01558 {
01559 public:
01560 const QString &name(void) const
01561 {
01562 static QString name = QCoreApplication::translate("Visualizers",
01563 "AlbumArt");
01564 return name;
01565 }
01566
01567 uint plugins(QStringList *list) const
01568 {
01569 *list << name();
01570 return 1;
01571 }
01572
01573 VisualBase *create(MainVisual *parent, const QString &pluginName) const
01574 {
01575 (void) parent;
01576 (void)pluginName;
01577 return new AlbumArt();
01578 }
01579 }AlbumArtFactory;
01580
01581 Blank::Blank()
01582 : VisualBase(true)
01583 {
01584 m_fps = 1;
01585 }
01586
01587 Blank::~Blank()
01588 {
01589 }
01590
01591 void Blank::resize(const QSize &newsize)
01592 {
01593 size = newsize;
01594 }
01595
01596
01597 bool Blank::process(VisualNode *node)
01598 {
01599 node = node;
01600 return false;
01601 }
01602
01603 bool Blank::draw(QPainter *p, const QColor &back)
01604 {
01605
01606 p->fillRect(0, 0, size.width(), size.height(), back);
01607 return true;
01608 }
01609
01610 static class BlankFactory : public VisFactory
01611 {
01612 public:
01613 const QString &name(void) const
01614 {
01615 static QString name = QCoreApplication::translate("Visualizers",
01616 "Blank");
01617 return name;
01618 }
01619
01620 uint plugins(QStringList *list) const
01621 {
01622 *list << name();
01623 return 1;
01624 }
01625
01626 VisualBase *create(MainVisual *parent, const QString &pluginName) const
01627 {
01628 (void)parent;
01629 (void)pluginName;
01630 return new Blank();
01631 }
01632 }BlankFactory;