MythTV  master
visualize.cpp
Go to the documentation of this file.
1 /*
2  visualize.cpp
3 
4  (c) 2003 Thor Sigvaldason and Isaac Richards
5  VERY closely based on code from mq3 by Brad Hughes
6 
7  Part of the mythTV project
8 
9  music visualizers
10 */
11 
12 // C
13 #include <cmath>
14 
15 // C++
16 #include <iostream>
17 using namespace std;
18 
19 // Qt
20 #include <QCoreApplication>
21 #include <QPainter>
22 #include <QImage>
23 
24 // MythTV
25 #include <mythdbcon.h>
26 #include <mythcontext.h>
27 #include <mythuihelper.h>
28 #include <remotefile.h>
29 #include <musicmetadata.h>
30 
31 // mythmusic
32 #include "mainvisual.h"
33 #include "visualize.h"
34 #include "inlines.h"
35 #include "decoder.h"
36 #include "musicplayer.h"
37 
38 #define FFTW_N 512
39 // static_assert(FFTW_N==SAMPLES_DEFAULT_SIZE)
40 
41 
43 
44 VisualBase::VisualBase(bool screensaverenable)
45  : m_xscreensaverenable(screensaverenable)
46 {
49 }
50 
52 {
53  //
54  // This is only here so
55  // that derived classes
56  // can destruct properly
57  //
60 }
61 
62 
63 void VisualBase::drawWarning(QPainter *p, const QColor &back, const QSize &size, const QString& warning, int fontSize)
64 {
65  p->fillRect(0, 0, size.width(), size.height(), back);
66  p->setPen(Qt::white);
67  QFont font = GetMythUI()->GetMediumFont();
68  font.setPointSizeF(fontSize * (size.width() / 800.0));
69  p->setFont(font);
70 
71  p->drawText(0, 0, size.width(), size.height(), Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextWordWrap, warning);
72 }
73 
75 // LogScale
76 
77 LogScale::LogScale(int maxscale, int maxrange)
78 {
79  setMax(maxscale, maxrange);
80 }
81 
83 {
84  delete [] m_indices;
85 }
86 
87 void LogScale::setMax(int maxscale, int maxrange)
88 {
89  if (maxscale == 0 || maxrange == 0)
90  return;
91 
92  m_s = maxscale;
93  m_r = maxrange;
94 
95  delete [] m_indices;
96 
97  double alpha;
98  long double domain = (long double) maxscale;
99  long double range = (long double) maxrange;
100  long double x = 1.0;
101  long double dx = 1.0;
102  long double e4 = 1.0E-8;
103 
104  m_indices = new int[maxrange];
105  for (int i = 0; i < maxrange; i++)
106  m_indices[i] = 0;
107 
108  // initialize log scale
109  for (uint i=0; i<10000 && (std::abs(dx) > e4); i++)
110  {
111  double t = std::log((domain + x) / x);
112  double y = (x * t) - range;
113  double yy = t - (domain / (x + domain));
114  dx = y / yy;
115  x -= dx;
116  }
117 
118  alpha = x;
119  for (int i = 1; i < (int) domain; i++)
120  {
121  int scaled = (int) floor(0.5 + (alpha * log((double(i) + alpha) / alpha)));
122  if (scaled < 1)
123  scaled = 1;
124  if (m_indices[scaled - 1] < i)
125  m_indices[scaled - 1] = i;
126  }
127 }
128 
129 int LogScale::operator[](int index)
130 {
131  return m_indices[index];
132 }
133 
135 // StereoScope
136 
138 {
139  m_fps = 45;
140 }
141 
142 void StereoScope::resize( const QSize &newsize )
143 {
144  m_size = newsize;
145 
146  auto os = m_magnitudes.size();
147  m_magnitudes.resize( m_size.width() * 2 );
148  for ( ; os < m_magnitudes.size(); os++ )
149  m_magnitudes[os] = 0.0;
150 }
151 
153 {
154  bool allZero = true;
155 
156 
157  if (node)
158  {
159  double index = 0;
160  double const step = (double)SAMPLES_DEFAULT_SIZE / m_size.width();
161  for ( int i = 0; i < m_size.width(); i++)
162  {
163  unsigned long indexTo = (unsigned long)(index + step);
164  if (indexTo == (unsigned long)(index))
165  indexTo = (unsigned long)(index + 1);
166 
167  double valL = 0, valR = 0;
168 #if RUBBERBAND
169  if ( m_rubberband ) {
170  valL = m_magnitudes[ i ];
171  valR = m_magnitudes[ i + m_size.width() ];
172  if (valL < 0.) {
173  valL += m_falloff;
174  if ( valL > 0. )
175  valL = 0.;
176  }
177  else
178  {
179  valL -= m_falloff;
180  if ( valL < 0. )
181  valL = 0.;
182  }
183  if (valR < 0.)
184  {
185  valR += m_falloff;
186  if ( valR > 0. )
187  valR = 0.;
188  }
189  else
190  {
191  valR -= m_falloff;
192  if ( valR < 0. )
193  valR = 0.;
194  }
195  }
196 #endif
197  for (unsigned long s = (unsigned long)index; s < indexTo && s < node->m_length; s++)
198  {
199  double adjHeight = static_cast<double>(m_size.height()) / 4.0;
200  double tmpL = ( ( node->m_left ? static_cast<double>(node->m_left[s]) : 0.) *
201  adjHeight ) / 32768.0;
202  double tmpR = ( ( node->m_right ? static_cast<double>(node->m_right[s]) : 0.) *
203  adjHeight ) / 32768.0;
204  if (tmpL > 0)
205  valL = (tmpL > valL) ? tmpL : valL;
206  else
207  valL = (tmpL < valL) ? tmpL : valL;
208  if (tmpR > 0)
209  valR = (tmpR > valR) ? tmpR : valR;
210  else
211  valR = (tmpR < valR) ? tmpR : valR;
212  }
213 
214  if (valL != 0. || valR != 0.)
215  allZero = false;
216 
217  m_magnitudes[ i ] = valL;
218  m_magnitudes[ i + m_size.width() ] = valR;
219 
220  index = index + step;
221  }
222 #if RUBBERBAND
223  }
224  else if (m_rubberband)
225  {
226  for ( int i = 0; i < m_size.width(); i++)
227  {
228  double valL = m_magnitudes[ i ];
229  if (valL < 0) {
230  valL += 2;
231  if (valL > 0.)
232  valL = 0.;
233  } else {
234  valL -= 2;
235  if (valL < 0.)
236  valL = 0.;
237  }
238 
239  double valR = m_magnitudes[ i + m_size.width() ];
240  if (valR < 0.) {
241  valR += m_falloff;
242  if (valR > 0.)
243  valR = 0.;
244  }
245  else
246  {
247  valR -= m_falloff;
248  if (valR < 0.)
249  valR = 0.;
250  }
251 
252  if (valL != 0. || valR != 0.)
253  allZero = false;
254 
255  m_magnitudes[ i ] = valL;
256  m_magnitudes[ i + m_size.width() ] = valR;
257  }
258 #endif
259  }
260  else
261  {
262  for ( int i = 0; (unsigned) i < m_magnitudes.size(); i++ )
263  m_magnitudes[ i ] = 0.;
264  }
265 
266  return allZero;
267 }
268 
269 bool StereoScope::draw( QPainter *p, const QColor &back )
270 {
271  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
272  for ( int i = 1; i < m_size.width(); i++ )
273  {
274 #if TWOCOLOUR
275  double r, g, b, per;
276 
277  // left
278  per = ( static_cast<double>(m_magnitudes[i]) * 2.0 ) /
279  ( static_cast<double>(m_size.height()) / 4.0 );
280  if (per < 0.0)
281  per = -per;
282  if (per > 1.0)
283  per = 1.0;
284  else if (per < 0.0)
285  per = 0.0;
286 
287  r = m_startColor.red() + (m_targetColor.red() -
288  m_startColor.red()) * (per * per);
289  g = m_startColor.green() + (m_targetColor.green() -
290  m_startColor.green()) * (per * per);
291  b = m_startColor.blue() + (m_targetColor.blue() -
292  m_startColor.blue()) * (per * per);
293 
294  if (r > 255.0)
295  r = 255.0;
296  else if (r < 0.0)
297  r = 0;
298 
299  if (g > 255.0)
300  g = 255.0;
301  else if (g < 0.0)
302  g = 0;
303 
304  if (b > 255.0)
305  b = 255.0;
306  else if (b < 0.0)
307  b = 0;
308 
309  p->setPen( QColor( int(r), int(g), int(b) ) );
310 #else
311  p->setPen(Qt::red);
312 #endif
313  double adjHeight = static_cast<double>(m_size.height()) / 4.0;
314  p->drawLine( i - 1,
315  (int)(adjHeight + m_magnitudes[i - 1]),
316  i,
317  (int)(adjHeight + m_magnitudes[i]));
318 
319 #if TWOCOLOUR
320  // right
321  per = ( static_cast<double>(m_magnitudes[ i + m_size.width() ]) * 2 ) /
322  adjHeight;
323  if (per < 0.0)
324  per = -per;
325  if (per > 1.0)
326  per = 1.0;
327  else if (per < 0.0)
328  per = 0.0;
329 
330  r = m_startColor.red() + (m_targetColor.red() -
331  m_startColor.red()) * (per * per);
332  g = m_startColor.green() + (m_targetColor.green() -
333  m_startColor.green()) * (per * per);
334  b = m_startColor.blue() + (m_targetColor.blue() -
335  m_startColor.blue()) * (per * per);
336 
337  if (r > 255.0)
338  r = 255.0;
339  else if (r < 0.0)
340  r = 0;
341 
342  if (g > 255.0)
343  g = 255.0;
344  else if (g < 0.0)
345  g = 0;
346 
347  if (b > 255.0)
348  b = 255.0;
349  else if (b < 0.0)
350  b = 0;
351 
352  p->setPen( QColor( int(r), int(g), int(b) ) );
353 #else
354  p->setPen(Qt::red);
355 #endif
356  adjHeight = static_cast<double>(m_size.height()) * 3.0 / 4.0;
357  p->drawLine( i - 1,
358  (int)(adjHeight + m_magnitudes[i + m_size.width() - 1]),
359  i,
360  (int)(adjHeight + m_magnitudes[i + m_size.width()]));
361  }
362 
363  return true;
364 }
365 
367 // MonoScope
368 
370 {
371  bool allZero = true;
372 
373  if (node)
374  {
375  double index = 0;
376  double const step = (double)SAMPLES_DEFAULT_SIZE / m_size.width();
377  for (int i = 0; i < m_size.width(); i++)
378  {
379  unsigned long indexTo = (unsigned long)(index + step);
380  if (indexTo == (unsigned long)index)
381  indexTo = (unsigned long)(index + 1);
382 
383  double val = 0;
384 #if RUBBERBAND
385  if ( m_rubberband )
386  {
387  val = m_magnitudes[ i ];
388  if (val < 0.)
389  {
390  val += m_falloff;
391  if ( val > 0. )
392  {
393  val = 0.;
394  }
395  }
396  else
397  {
398  val -= m_falloff;
399  if ( val < 0. )
400  {
401  val = 0.;
402  }
403  }
404  }
405 #endif
406  for (unsigned long s = (unsigned long)index; s < indexTo && s < node->m_length; s++)
407  {
408  double tmp = ( static_cast<double>(node->m_left[s]) +
409  (node->m_right ? static_cast<double>(node->m_right[s]) : 0.0) *
410  ( static_cast<double>(m_size.height()) / 2.0 ) ) / 65536.0;
411  if (tmp > 0)
412  {
413  val = (tmp > val) ? tmp : val;
414  }
415  else
416  {
417  val = (tmp < val) ? tmp : val;
418  }
419  }
420 
421  if ( val != 0. )
422  {
423  allZero = false;
424  }
425  m_magnitudes[ i ] = val;
426  index = index + step;
427  }
428  }
429 #if RUBBERBAND
430  else if (m_rubberband)
431  {
432  for (int i = 0; i < m_size.width(); i++) {
433  double val = m_magnitudes[ i ];
434  if (val < 0) {
435  val += 2;
436  if (val > 0.)
437  val = 0.;
438  } else {
439  val -= 2;
440  if (val < 0.)
441  val = 0.;
442  }
443 
444  if ( val != 0. )
445  allZero = false;
446  m_magnitudes[ i ] = val;
447  }
448  }
449 #endif
450  else
451  {
452  for (int i = 0; i < m_size.width(); i++ )
453  m_magnitudes[ i ] = 0.;
454  }
455 
456  return allZero;
457 }
458 
459 bool MonoScope::draw( QPainter *p, const QColor &back )
460 {
461  p->fillRect( 0, 0, m_size.width(), m_size.height(), back );
462  for ( int i = 1; i < m_size.width(); i++ ) {
463 #if TWOCOLOUR
464  double r, g, b, per;
465 
466  per = double( m_magnitudes[ i ] ) /
467  double( m_size.height() / 4 );
468  if (per < 0.0)
469  per = -per;
470  if (per > 1.0)
471  per = 1.0;
472  else if (per < 0.0)
473  per = 0.0;
474 
475  r = m_startColor.red() + (m_targetColor.red() -
476  m_startColor.red()) * (per * per);
477  g = m_startColor.green() + (m_targetColor.green() -
478  m_startColor.green()) * (per * per);
479  b = m_startColor.blue() + (m_targetColor.blue() -
480  m_startColor.blue()) * (per * per);
481 
482  if (r > 255.0)
483  r = 255.0;
484  else if (r < 0.0)
485  r = 0;
486 
487  if (g > 255.0)
488  g = 255.0;
489  else if (g < 0.0)
490  g = 0;
491 
492  if (b > 255.0)
493  b = 255.0;
494  else if (b < 0.0)
495  b = 0;
496 
497  p->setPen(QColor(int(r), int(g), int(b)));
498 #else
499  p->setPen(Qt::red);
500 #endif
501  double adjHeight = static_cast<double>(m_size.height()) / 2.0;
502  p->drawLine( i - 1,
503  (int)(adjHeight + m_magnitudes[ i - 1 ]),
504  i,
505  (int)(adjHeight + m_magnitudes[ i ] ));
506  }
507 
508  return true;
509 }
510 
512 // StereoScopeFactory
513 
514 static class StereoScopeFactory : public VisFactory
515 {
516  public:
517  const QString &name(void) const override // VisFactory
518  {
519  static QString name = QCoreApplication::translate("Visualizers",
520  "StereoScope");
521  return name;
522  }
523 
524  uint plugins(QStringList *list) const override // VisFactory
525  {
526  *list << name();
527  return 1;
528  }
529 
530  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
531  {
532  return new StereoScope();
533  }
535 
536 
538 // MonoScopeFactory
539 
540 static class MonoScopeFactory : public VisFactory
541 {
542  public:
543  const QString &name(void) const override // VisFactory
544  {
545  static QString name = QCoreApplication::translate("Visualizers",
546  "MonoScope");
547  return name;
548  }
549 
550  uint plugins(QStringList *list) const override // VisFactory
551  {
552  *list << name();
553  return 1;
554  }
555 
556  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
557  {
558  return new MonoScope();
559  }
561 
563 // Spectrum
564 //
565 // NOTE: This visualiser requires mythplugins to be compiled with --enable-fft
566 
567 #if FFTW3_SUPPORT
568 Spectrum::Spectrum()
569 {
570  LOG(VB_GENERAL, LOG_INFO, QString("Spectrum : Being Initialised"));
571 
572  m_fps = 15;
573 
574  m_lin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
575  m_rin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
576  m_lout = (myth_fftw_complex*) av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
577  m_rout = (myth_fftw_complex*) av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
578 
579  m_lplan = fftw_plan_dft_r2c_1d(FFTW_N, m_lin, (myth_fftw_complex_cast*)m_lout, FFTW_MEASURE);
580  m_rplan = fftw_plan_dft_r2c_1d(FFTW_N, m_rin, (myth_fftw_complex_cast*)m_rout, FFTW_MEASURE);
581 }
582 
583 Spectrum::~Spectrum()
584 {
585  if (m_lin)
586  av_free(m_lin);
587  if (m_rin)
588  av_free(m_rin);
589  if (m_lout)
590  av_free(m_lout);
591  if (m_rout)
592  av_free(m_rout);
593 
594  fftw_destroy_plan(m_lplan);
595  fftw_destroy_plan(m_rplan);
596 }
597 
598 void Spectrum::resize(const QSize &newsize)
599 {
600  // Just change internal data about the
601  // size of the pixmap to be drawn (ie. the
602  // size of the screen) and the logically
603  // ensuing number of up/down bars to hold
604  // the audio magnitudes
605 
606  m_size = newsize;
607 
608  m_analyzerBarWidth = m_size.width() / 64;
609 
610  if (m_analyzerBarWidth < 6)
611  m_analyzerBarWidth = 6;
612 
613  m_scale.setMax(192, m_size.width() / m_analyzerBarWidth);
614 
615  m_rects.resize( m_scale.range() );
616  unsigned int i = 0;
617  int w = 0;
618  for (; i < (uint)m_rects.size(); i++, w += m_analyzerBarWidth)
619  {
620  m_rects[i].setRect(w, m_size.height() / 2, m_analyzerBarWidth - 1, 1);
621  }
622 
623  unsigned int os = m_magnitudes.size();
624  m_magnitudes.resize( m_scale.range() * 2 );
625  for (; os < (uint)m_magnitudes.size(); os++)
626  {
627  m_magnitudes[os] = 0.0;
628  }
629 
630  m_scaleFactor = ( static_cast<double>(m_size.height()) / 2.0 ) /
631  log( static_cast<double>(FFTW_N) );
632 }
633 
634 template<typename T> T sq(T a) { return a*a; };
635 
636 bool Spectrum::process(VisualNode *node)
637 {
638  // Take a bunch of data in *node
639  // and break it down into spectrum
640  // values
641  bool allZero = true;
642 
643  uint i;
644  long w = 0, index;
645  QRect *rectsp = m_rects.data();
646  double *magnitudesp = m_magnitudes.data();
647 
648  if (node)
649  {
650  i = node->m_length;
651  if (i > FFTW_N)
652  i = FFTW_N;
653  fast_real_set_from_short(m_lin, node->m_left, i);
654  if (node->m_right)
655  fast_real_set_from_short(m_rin, node->m_right, i);
656  }
657  else
658  i = 0;
659 
660  fast_reals_set(m_lin + i, m_rin + i, 0, FFTW_N - i);
661 
662  fftw_execute(m_lplan);
663  fftw_execute(m_rplan);
664 
665  index = 1;
666 
667  for (i = 0; (int)i < m_rects.size(); i++, w += m_analyzerBarWidth)
668  {
669  // The 1D output is Hermitian symmetric (Yk = Yn-k) so Yn = Y0 etc.
670  // The dft_r2c_1d plan doesn't output these redundant values
671  // and furthermore they're not allocated in the ctor
672  double tmp = 2 * sq(real(m_lout[index])); // + sq(real(m_lout[FFTW_N - index]));
673  double magL = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
674 
675  tmp = 2 * sq(real(m_rout[index])); // + sq(real(m_rout[FFTW_N - index]));
676  double magR = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
677 
678 
679  double adjHeight = static_cast<double>(m_size.height()) / 2.0;
680  if (magL > adjHeight)
681  {
682  magL = adjHeight;
683  }
684  if (magL < magnitudesp[i])
685  {
686  tmp = magnitudesp[i] - m_falloff;
687  if ( tmp < magL )
688  {
689  tmp = magL;
690  }
691  magL = tmp;
692  }
693  if (magL < 1.)
694  {
695  magL = 1.;
696  }
697 
698  if (magR > adjHeight)
699  {
700  magR = adjHeight;
701  }
702  if (magR < magnitudesp[i + m_scale.range()])
703  {
704  tmp = magnitudesp[i + m_scale.range()] - m_falloff;
705  if ( tmp < magR )
706  {
707  tmp = magR;
708  }
709  magR = tmp;
710  }
711  if (magR < 1.)
712  {
713  magR = 1.;
714  }
715 
716  if (magR != 1 || magL != 1)
717  {
718  allZero = false;
719  }
720 
721  magnitudesp[i] = magL;
722  magnitudesp[i + m_scale.range()] = magR;
723  rectsp[i].setTop( m_size.height() / 2 - int( magL ) );
724  rectsp[i].setBottom( m_size.height() / 2 + int( magR ) );
725 
726  index = m_scale[i];
727  }
728 
729  Q_UNUSED(allZero);
730  return false;
731 }
732 
733 double Spectrum::clamp(double cur, double max, double min)
734 {
735  if (cur > max)
736  cur = max;
737  if (cur < min)
738  cur = min;
739  return cur;
740 }
741 
742 bool Spectrum::draw(QPainter *p, const QColor &back)
743 {
744  // This draws on a pixmap owned by MainVisual.
745  //
746  // In other words, this is not a Qt Widget, it
747  // just uses some Qt methods to draw on a pixmap.
748  // MainVisual then bitblts that onto the screen.
749 
750  QRect *rectsp = m_rects.data();
751 
752  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
753  for (uint i = 0; i < (uint)m_rects.size(); i++)
754  {
755  double per = double( rectsp[i].height() - 2 ) / double( m_size.height() );
756 
757  per = clamp(per, 1.0, 0.0);
758 
759  double r = m_startColor.red() +
760  (m_targetColor.red() - m_startColor.red()) * (per * per);
761  double g = m_startColor.green() +
762  (m_targetColor.green() - m_startColor.green()) * (per * per);
763  double b = m_startColor.blue() +
764  (m_targetColor.blue() - m_startColor.blue()) * (per * per);
765 
766  r = clamp(r, 255.0, 0.0);
767  g = clamp(g, 255.0, 0.0);
768  b = clamp(b, 255.0, 0.0);
769 
770  if(rectsp[i].height() > 4)
771  p->fillRect(rectsp[i], QColor(int(r), int(g), int(b)));
772  }
773 
774  return true;
775 }
776 
777 static class SpectrumFactory : public VisFactory
778 {
779  public:
780  const QString &name(void) const override // VisFactory
781  {
782  static QString name = QCoreApplication::translate("Visualizers",
783  "Spectrum");
784  return name;
785  }
786 
787  uint plugins(QStringList *list) const override // VisFactory
788  {
789  *list << name();
790  return 1;
791  }
792 
793  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
794  {
795  return new Spectrum();
796  }
797 }SpectrumFactory;
798 
800 // Squares
801 //
802 // NOTE: This visualiser requires mythplugins to be compiled with --enable-fftw
803 
804 Squares::Squares()
805 {
806  m_fake_height = m_number_of_squares * m_analyzerBarWidth;
807 }
808 
809 void Squares::resize (const QSize &newsize) {
810  // Trick the spectrum analyzer into calculating 16 rectangles
811  Spectrum::resize (QSize (m_fake_height, m_fake_height));
812  // We have our own copy, Spectrum has it's own...
813  m_actualSize = newsize;
814 }
815 
816 void Squares::drawRect(QPainter *p, QRect *rect, int i, int c, int w, int h)
817 {
818  double r, g, b, per;
819  int correction = (m_actualSize.width() % m_rects.size ()) / 2;
820  int x = ((i / 2) * w) + correction;
821  int y;
822 
823  if (i % 2 == 0)
824  {
825  y = c - h;
826  per = double(m_fake_height - rect->top()) / double(m_fake_height);
827  }
828  else
829  {
830  y = c;
831  per = double(rect->bottom()) / double(m_fake_height);
832  }
833 
834  per = clamp(per, 1.0, 0.0);
835 
836  r = m_startColor.red() +
837  (m_targetColor.red() - m_startColor.red()) * (per * per);
838  g = m_startColor.green() +
839  (m_targetColor.green() - m_startColor.green()) * (per * per);
840  b = m_startColor.blue() +
841  (m_targetColor.blue() - m_startColor.blue()) * (per * per);
842 
843  r = clamp(r, 255.0, 0.0);
844  g = clamp(g, 255.0, 0.0);
845  b = clamp(b, 255.0, 0.0);
846 
847  p->fillRect (x, y, w, h, QColor (int(r), int(g), int(b)));
848 }
849 
850 bool Squares::draw(QPainter *p, const QColor &back)
851 {
852  p->fillRect (0, 0, m_actualSize.width(), m_actualSize.height(), back);
853  int w = m_actualSize.width() / (m_rects.size() / 2);
854  int h = w;
855  int center = m_actualSize.height() / 2;
856 
857  QRect *rectsp = m_rects.data();
858  for (uint i = 0; i < (uint)m_rects.size(); i++)
859  drawRect(p, &(rectsp[i]), i, center, w, h);
860 
861  return true;
862 }
863 
864 static class SquaresFactory : public VisFactory
865 {
866  public:
867  const QString &name(void) const override // VisFactory
868  {
869  static QString name = QCoreApplication::translate("Visualizers",
870  "Squares");
871  return name;
872  }
873 
874  uint plugins(QStringList *list) const override // VisFactory
875  {
876  *list << name();
877  return 1;
878  }
879 
880  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
881  {
882  return new Squares();
883  }
884 }SquaresFactory;
885 
886 #endif // FFTW3_SUPPORT
887 
889 {
890  // Setup the "magical" audio coefficients
891  // required by the Goetzel Algorithm
892 
893  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Initialised"));
894 
895  m_piano_data = (piano_key_data *) malloc(sizeof(piano_key_data) * PIANO_N);
896  m_audio_data = (piano_audio *) malloc(sizeof(piano_audio) * PIANO_AUDIO_SIZE);
897 
898  double sample_rate = 44100.0; // TODO : This should be obtained from gPlayer (likely candidate...)
899 
900  m_fps = 20; // This is the display frequency. We're capturing all audio chunks by defining .process_undisplayed() though.
901 
902  double concert_A = 440.0;
903  double semi_tone = pow(2.0, 1.0/12.0);
904 
905  /* Lowest note on piano is 4 octaves below concert A */
906  double bottom_A = concert_A / 2.0 / 2.0 / 2.0 / 2.0;
907 
908  unsigned int key;
909  double current_freq = bottom_A;
910  for (key = 0; key < PIANO_N; key++)
911  {
912  // This is constant through time
913  m_piano_data[key].coeff = (goertzel_data)(2.0 * cos(2.0 * M_PI * current_freq / sample_rate));
914 
915  // Want 20 whole cycles of the current waveform at least
916  double samples_required = sample_rate/current_freq * 20.0;
917  if (samples_required > sample_rate/4.0)
918  {
919  // For the really low notes, 4 updates a second is good enough...
920  samples_required = sample_rate/4.0;
921  }
922  if (samples_required < sample_rate/(double)m_fps * 0.75)
923  { // For the high notes, use as many samples as we need in a display_fps
924  samples_required = sample_rate/(double)m_fps * 0.75;
925  }
926  m_piano_data[key].samples_process_before_display_update = (int)samples_required;
927  m_piano_data[key].is_black_note = false; // Will be put right in .resize()
928 
929  current_freq *= semi_tone;
930  }
931 
932  zero_analysis();
933 }
934 
936 {
937  if (m_piano_data)
938  free(m_piano_data);
939  if (m_audio_data)
940  free(m_audio_data);
941 }
942 
944 {
945  unsigned int key;
946  for (key = 0; key < PIANO_N; key++)
947  {
948  // These get updated continously, and must be stored between chunks of audio data
949  m_piano_data[key].q2 = 0.0F;
950  m_piano_data[key].q1 = 0.0F;
951  m_piano_data[key].magnitude = 0.0F;
953  (goertzel_data)(PIANO_RMS_NEGLIGIBLE*PIANO_RMS_NEGLIGIBLE); // This is a guess - will be quickly overwritten
954 
956  }
957  m_offset_processed = 0;
958 }
959 
960 void Piano::resize(const QSize &newsize)
961 {
962  // Just change internal data about the
963  // size of the pixmap to be drawn (ie. the
964  // size of the screen) and the logically
965  // ensuing number of up/down bars to hold
966  // the audio magnitudes
967 
968  m_size = newsize;
969 
970  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Resized"));
971 
972  zero_analysis();
973 
974  // There are 88-36=52 white notes on piano keyboard
975  double key_unit_size = (double)m_size.width() / 54.0; // One white key extra spacing, if possible
976  if (key_unit_size < 10.0) // Keys have to be at least this many pixels wide
977  key_unit_size = 10.0;
978 
979  double white_width_pct = .8;
980  double black_width_pct = .6;
981  double black_offset_pct = .05;
982 
983  double white_height_pct = 6;
984  double black_height_pct = 4;
985 
986  // This is the starting position of the keyboard (may be beyond LHS)
987  // - actually position of C below bottom A (will be added to...). This is 4 octaves below middle C.
988  double left = (double)m_size.width() / 2.0 - (4.0*7.0 + 3.5) * key_unit_size; // The extra 3.5 centers 'F' inthe middle of the screen
989  double top_of_keys = (double)m_size.height() / 2.0 - key_unit_size * white_height_pct / 2.0; // Vertically center keys
990 
991  m_rects.resize(PIANO_N);
992 
993  unsigned int key;
994  for (key = 0; key < PIANO_N; key++)
995  {
996  int note = ((int)key - 3 + 12) % 12; // This means that C=0, C#=1, D=2, etc (since lowest note is bottom A)
997  if (note == 0) // If we're on a 'C', move the left 'cursor' over an octave
998  {
999  left += key_unit_size*7.0;
1000  }
1001 
1002  double center = 0.0;
1003  double offset = 0.0;
1004  bool is_black = false;
1005 
1006  switch (note)
1007  {
1008  case 0: center = 0.5; break;
1009  case 1: center = 1.0; is_black = true; offset = -1; break;
1010  case 2: center = 1.5; break;
1011  case 3: center = 2.0; is_black = true; offset = +1; break;
1012  case 4: center = 2.5; break;
1013  case 5: center = 3.5; break;
1014  case 6: center = 4.0; is_black = true; offset = -2; break;
1015  case 7: center = 4.5; break;
1016  case 8: center = 5.0; is_black = true; offset = 0; break;
1017  case 9: center = 5.5; break;
1018  case 10: center = 6.0; is_black = true; offset = 2; break;
1019  case 11: center = 6.5; break;
1020  }
1021  m_piano_data[key].is_black_note = is_black;
1022 
1023  double width = (is_black ? black_width_pct:white_width_pct) * key_unit_size;
1024  double height = (is_black? black_height_pct:white_height_pct) * key_unit_size;
1025 
1026  m_rects[key].setRect(
1027  left + center * key_unit_size // Basic position of left side of key
1028  - width / 2.0 // Less half the width
1029  + (is_black ? (offset * black_offset_pct * key_unit_size):0.0), // And jiggle the positions of the black keys for aethetic reasons
1030  top_of_keys, // top
1031  width, // width
1032  height // height
1033  );
1034  }
1035 
1036  m_magnitude.resize(PIANO_N);
1037  for (key = 0; key < (uint)m_magnitude.size(); key++)
1038  {
1039  m_magnitude[key] = 0.0;
1040  }
1041 }
1042 
1043 unsigned long Piano::getDesiredSamples(void)
1044 {
1045  // We want all the data! (within reason)
1046  // typical observed values are 882 -
1047  // 12.5 chunks of data per second from 44100Hz signal : Sampled at 50Hz, lots of 4, see :
1048  // mythtv/libs/libmyth/audio/audiooutputbase.cpp :: AudioOutputBase::AddData
1049  // See : mythtv/mythplugins/mythmusic/mythmusic/avfdecoder.cpp "20ms worth"
1050  return (unsigned long) PIANO_AUDIO_SIZE; // Maximum we can be given
1051 }
1052 
1054 {
1055  //LOG(VB_GENERAL, LOG_INFO, QString("Piano : Processing undisplayed node"));
1056  return process_all_types(node, false);
1057 }
1058 
1060 {
1061  //LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Processing node for DISPLAY"));
1062 // return process_all_types(node, true);
1063  process_all_types(node, true);
1064  return false;
1065 }
1066 
1067 bool Piano::process_all_types(VisualNode *node, bool /*this_will_be_displayed*/)
1068 {
1069  // Take a bunch of data in *node and break it down into piano key spectrum values
1070  // NB: Remember the state data between calls, so as to accumulate more accurate results.
1071  bool allZero = true;
1072  uint n;
1073  goertzel_data magnitude2, magnitude_av;
1074 
1075  if (node)
1076  {
1077  piano_audio short_to_bounded = 32768.0F;
1078 
1079  // Detect start of new song (current node more than 10s earlier than already seen)
1080  if (node->m_offset + 10000 < m_offset_processed)
1081  {
1082  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Node offset=%1 too far backwards : NEW SONG").arg(node->m_offset));
1083  zero_analysis();
1084  }
1085 
1086  // Check whether we've seen this node (more recently than 10secs ago)
1087  if (node->m_offset <= m_offset_processed)
1088  {
1089  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Already seen node offset=%1, returning without processing").arg(node->m_offset));
1090  return allZero; // Nothing to see here - the server can stop if it wants to
1091  }
1092 
1093  //LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Processing node offset=%1, size=%2").arg(node->m_offset).arg(node->m_length));
1094  n = node->m_length;
1095 
1096  if (node->m_right) // Preprocess the data into a combined middle channel, if we have stereo data
1097  {
1098  for (uint i = 0; i < n; i++)
1099  {
1100  m_audio_data[i] = ((piano_audio)node->m_left[i] + (piano_audio)node->m_right[i]) / 2.0F / short_to_bounded;
1101  }
1102  }
1103  else // This is only one channel of data
1104  {
1105  for (uint i = 0; i < n; i++)
1106  {
1107  m_audio_data[i] = (piano_audio)node->m_left[i] / short_to_bounded;
1108  }
1109  }
1110  }
1111  else
1112  {
1113  LOG(VB_GENERAL, LOG_DEBUG, QString("Hit an empty node, and returning empty-handed"));
1114  return allZero; // Nothing to see here - the server can stop if it wants to
1115  }
1116 
1117  for (uint key = 0; key < PIANO_N; key++)
1118  {
1119  goertzel_data coeff = m_piano_data[key].coeff;
1120  goertzel_data q2 = m_piano_data[key].q2;
1121  goertzel_data q1 = m_piano_data[key].q1;
1122 
1123  for (uint i = 0; i < n; i++)
1124  {
1125  goertzel_data q0 = coeff * q1 - q2 + m_audio_data[i];
1126  q2 = q1;
1127  q1 = q0;
1128  }
1129  m_piano_data[key].q2 = q2;
1130  m_piano_data[key].q1 = q1;
1131 
1132  m_piano_data[key].samples_processed += n;
1133 
1134  int n_samples = m_piano_data[key].samples_processed;
1135 
1136  // Only do this update if we've processed enough chunks for this key...
1137  if (n_samples > m_piano_data[key].samples_process_before_display_update)
1138  {
1139  magnitude2 = q1*q1 + q2*q2 - q1*q2*coeff;
1140 
1141 #if 0
1142  // This is RMS of signal
1143  magnitude_av = sqrt(magnitude2)/(goertzel_data)n_samples; // Should be 0<magnitude_av<.5
1144 #else
1145  // This is pure magnitude of signal
1146  magnitude_av = magnitude2/(goertzel_data)n_samples/(goertzel_data)n_samples; // Should be 0<magnitude_av<.25
1147 #endif
1148 
1149 #if 0
1150  // Take logs everywhere, and shift up to [0, ??]
1151  if(magnitude_av > 0.0F)
1152  {
1153  magnitude_av = log(magnitude_av);
1154  }
1155  else
1156  {
1157  magnitude_av = PIANO_MIN_VOL;
1158  }
1159  magnitude_av -= PIANO_MIN_VOL;
1160 
1161  if (magnitude_av < 0.0F)
1162  {
1163  magnitude_av = 0.0;
1164  }
1165 #endif
1166 
1167  if (magnitude_av > (goertzel_data)0.01)
1168  {
1169  allZero = false;
1170  }
1171 
1172  m_piano_data[key].magnitude = magnitude_av; // Store this for later : We'll do the colours from this...
1173  if ( m_piano_data[key].max_magnitude_seen < magnitude_av)
1174  {
1175  m_piano_data[key].max_magnitude_seen = magnitude_av;
1176  }
1177  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Updated Key %1 from %2 samples, magnitude=%3")
1178  .arg(key).arg(n_samples).arg(magnitude_av));
1179 
1180  m_piano_data[key].samples_processed = 0; // Reset the counts, now that we've set the magnitude...
1181  m_piano_data[key].q1 = (goertzel_data)0.0;
1182  m_piano_data[key].q2 = (goertzel_data)0.0;
1183  }
1184  }
1185 
1186  if (node)
1187  {
1188  // All done now - record that we've done this offset
1189  m_offset_processed = node->m_offset;
1190  }
1191 
1192  return allZero;
1193 }
1194 
1195 double Piano::clamp(double cur, double max, double min)
1196 {
1197  if (cur > max)
1198  cur = max;
1199  if (cur < min)
1200  cur = min;
1201  return cur;
1202 }
1203 
1204 bool Piano::draw(QPainter *p, const QColor &back)
1205 {
1206  // This draws on a pixmap owned by MainVisual.
1207  //
1208  // In other words, this is not a Qt Widget, it
1209  // just uses some Qt methods to draw on a pixmap.
1210  // MainVisual then bitblts that onto the screen.
1211 
1212  QRect *rectsp = &m_rects[0];
1213  double *magnitudep = &m_magnitude[0];
1214 
1215  unsigned int key, n = PIANO_N;
1216  double r, g, b, per;
1217 
1218  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1219 
1220  // Protect maximum array length
1221  if(n > (uint)m_rects.size())
1222  n = (uint)m_rects.size();
1223 
1224  // Sweep up across the keys, making sure the max_magnitude_seen is at minimum X% of its neighbours
1225  double mag = PIANO_RMS_NEGLIGIBLE;
1226  for (key = 0; key < n; key++)
1227  {
1228  if (m_piano_data[key].max_magnitude_seen < static_cast<float>(mag))
1229  {
1230  // Spread the previous value to this key
1231  m_piano_data[key].max_magnitude_seen = mag;
1232  }
1233  else
1234  {
1235  // This key has seen better peaks, use this for the next one
1236  mag = m_piano_data[key].max_magnitude_seen;
1237  }
1238  mag *= PIANO_SPECTRUM_SMOOTHING;
1239  }
1240 
1241  // Similarly, down, making sure the max_magnitude_seen is at minimum X% of its neighbours
1242  mag = PIANO_RMS_NEGLIGIBLE;
1243  for (int key_i = n - 1; key_i >= 0; key_i--)
1244  {
1245  key = key_i; // Wow, this is to avoid a zany error for ((unsigned)0)--
1246  if (m_piano_data[key].max_magnitude_seen < static_cast<float>(mag))
1247  {
1248  // Spread the previous value to this key
1249  m_piano_data[key].max_magnitude_seen = mag;
1250  }
1251  else
1252  {
1253  // This key has seen better peaks, use this for the next one
1254  mag = m_piano_data[key].max_magnitude_seen;
1255  }
1256  mag *= PIANO_SPECTRUM_SMOOTHING;
1257  }
1258 
1259  // Now find the key that has been hit the hardest relative to its experience, and renormalize...
1260  // Set a minimum, to prevent divide-by-zero (and also all-pressed when music very quiet)
1261  double magnitude_max = PIANO_RMS_NEGLIGIBLE;
1262  for (key = 0; key < n; key++)
1263  {
1265  if (magnitude_max < mag)
1266  magnitude_max = mag;
1267 
1268  magnitudep[key] = mag;
1269  }
1270 
1271  // Deal with all the white keys first
1272  for (key = 0; key < n; key++)
1273  {
1274  if (m_piano_data[key].is_black_note)
1275  continue;
1276 
1277  per = magnitudep[key] / magnitude_max;
1278  per = clamp(per, 1.0, 0.0); // By construction, this should be unnecessary
1279 
1280  if (per < PIANO_KEYPRESS_TOO_LIGHT)
1281  per = 0.0; // Clamp to zero for lightly detected keys
1282  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Display key %1, magnitude=%2, seen=%3")
1283  .arg(key).arg(per*100.0).arg(m_piano_data[key].max_magnitude_seen));
1284 
1285  r = m_whiteStartColor.red() + (m_whiteTargetColor.red() - m_whiteStartColor.red()) * per;
1286  g = m_whiteStartColor.green() + (m_whiteTargetColor.green() - m_whiteStartColor.green()) * per;
1287  b = m_whiteStartColor.blue() + (m_whiteTargetColor.blue() - m_whiteStartColor.blue()) * per;
1288 
1289  p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
1290  }
1291 
1292  // Then overlay the black keys
1293  for (key = 0; key < n; key++)
1294  {
1295  if (!m_piano_data[key].is_black_note)
1296  continue;
1297 
1298  per = magnitudep[key]/magnitude_max;
1299  per = clamp(per, 1.0, 0.0); // By construction, this should be unnecessary
1300 
1301  if (per < PIANO_KEYPRESS_TOO_LIGHT)
1302  per = 0.0; // Clamp to zero for lightly detected keys
1303 
1304  r = m_blackStartColor.red() + (m_blackTargetColor.red() - m_blackStartColor.red()) * per;
1305  g = m_blackStartColor.green() + (m_blackTargetColor.green() - m_blackStartColor.green()) * per;
1306  b = m_blackStartColor.blue() + (m_blackTargetColor.blue() - m_blackStartColor.blue()) * per;
1307 
1308  p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
1309  }
1310 
1311  return true;
1312 }
1313 
1314 static class PianoFactory : public VisFactory
1315 {
1316  public:
1317  const QString &name(void) const override // VisFactory
1318  {
1319  static QString name = QCoreApplication::translate("Visualizers",
1320  "Piano");
1321  return name;
1322  }
1323 
1324  uint plugins(QStringList *list) const override // VisFactory
1325  {
1326  *list << name();
1327  return 1;
1328  }
1329 
1330  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1331  {
1332  return new Piano();
1333  }
1334 }PianoFactory;
1335 
1337  m_lastCycle(QDateTime::currentDateTime())
1338 {
1339  findFrontCover();
1340  m_fps = 1;
1341 }
1342 
1344 {
1345  if (!gPlayer->getCurrentMetadata())
1346  return;
1347 
1348  // if a front cover image is available show that first
1350  if (albumArt->getImage(IT_FRONTCOVER))
1352  else
1353  {
1354  // not available so just show the first image available
1355  if (albumArt->getImageCount() > 0)
1356  m_currImageType = albumArt->getImageAt(0)->m_imageType;
1357  else
1359  }
1360 }
1361 
1363 {
1364  if (!gPlayer->getCurrentMetadata())
1365  return false;
1366 
1368  int newType = m_currImageType;
1369 
1370  // If we only have one image there is nothing to cycle
1371  if (albumArt->getImageCount() > 1)
1372  {
1373  do
1374  {
1375  newType++;
1376  if (newType == IT_LAST)
1377  newType = IT_UNKNOWN;
1378  } while (!albumArt->getImage((ImageType) newType));
1379  }
1380 
1381  if (newType != m_currImageType)
1382  {
1383  m_currImageType = (ImageType) newType;
1384  m_lastCycle = QDateTime::currentDateTime();
1385  return true;
1386  }
1387 
1388  return false;
1389 }
1390 
1391 void AlbumArt::resize(const QSize &newsize)
1392 {
1393  m_size = newsize;
1394 }
1395 
1397 {
1398  return false;
1399 }
1400 
1401 void AlbumArt::handleKeyPress(const QString &action)
1402 {
1403  if (action == "SELECT")
1404  {
1405  if (gPlayer->getCurrentMetadata())
1406  {
1408  int newType = m_currImageType;
1409 
1410  if (albumArt.getImageCount() > 0)
1411  {
1412  newType++;
1413 
1414  while (!albumArt.getImage((ImageType) newType))
1415  {
1416  newType++;
1417  if (newType == IT_LAST)
1418  newType = IT_UNKNOWN;
1419  }
1420  }
1421 
1422  if (newType != m_currImageType)
1423  {
1424  m_currImageType = (ImageType) newType;
1425  // force an update
1426  m_cursize = QSize(0, 0);
1427  }
1428  }
1429  }
1430 }
1431 
1433 #define ALBUMARTCYCLETIME 10
1434 
1436 {
1437  // if the track has changed we need to update the image
1439  {
1441  findFrontCover();
1442  return true;
1443  }
1444 
1445  // if it's time to cycle to the next image we need to update the image
1446  if (m_lastCycle.addSecs(ALBUMARTCYCLETIME) < QDateTime::currentDateTime())
1447  {
1448  if (cycleImage())
1449  return true;
1450  }
1451 
1452  return false;
1453 }
1454 
1455 bool AlbumArt::draw(QPainter *p, const QColor &back)
1456 {
1457  if (needsUpdate())
1458  {
1459  QImage art;
1460  QString imageFilename = gPlayer->getCurrentMetadata()->getAlbumArtFile(m_currImageType);
1461 
1462  if (imageFilename.startsWith("myth://"))
1463  {
1464  RemoteFile *rf = new RemoteFile(imageFilename, false, false, 0);
1465 
1466  QByteArray data;
1467  bool ret = rf->SaveAs(data);
1468 
1469  delete rf;
1470 
1471  if (ret)
1472  art.loadFromData(data);
1473  }
1474  else
1475  if (!imageFilename.isEmpty())
1476  art.load(imageFilename);
1477 
1478  if (art.isNull())
1479  {
1480  m_cursize = m_size;
1481  m_image = QImage();
1482  }
1483  else
1484  {
1485  m_image = art.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1486  }
1487  }
1488 
1489  if (m_image.isNull())
1490  {
1491  drawWarning(p, back, m_size, tr("?"), 100);
1492  return true;
1493  }
1494 
1495  // Paint the image
1496  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1497  p->drawImage((m_size.width() - m_image.width()) / 2,
1498  (m_size.height() - m_image.height()) / 2,
1499  m_image);
1500 
1501  // Store our new size
1502  m_cursize = m_size;
1503 
1504  return true;
1505 }
1506 
1507 static class AlbumArtFactory : public VisFactory
1508 {
1509  public:
1510  const QString &name(void) const override // VisFactory
1511  {
1512  static QString name = QCoreApplication::translate("Visualizers",
1513  "AlbumArt");
1514  return name;
1515  }
1516 
1517  uint plugins(QStringList *list) const override // VisFactory
1518  {
1519  *list << name();
1520  return 1;
1521  }
1522 
1523  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1524  {
1525  return new AlbumArt();
1526  }
1528 
1530  : VisualBase(true)
1531 {
1532  m_fps = 1;
1533 }
1534 
1535 void Blank::resize(const QSize &newsize)
1536 {
1537  m_size = newsize;
1538 }
1539 
1540 
1542 {
1543  return false;
1544 }
1545 
1546 bool Blank::draw(QPainter *p, const QColor &back)
1547 {
1548  // Took me hours to work out this algorithm
1549  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1550  return true;
1551 }
1552 
1553 static class BlankFactory : public VisFactory
1554 {
1555  public:
1556  const QString &name(void) const override // VisFactory
1557  {
1558  static QString name = QCoreApplication::translate("Visualizers",
1559  "Blank");
1560  return name;
1561  }
1562 
1563  uint plugins(QStringList *list) const override // VisFactory
1564  {
1565  *list << name();
1566  return 1;
1567  }
1568 
1569  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1570  {
1571  return new Blank();
1572  }
1573 }BlankFactory;
piano_audio * m_audio_data
Definition: visualize.h:293
int operator[](int index) const
unsigned long getDesiredSamples(void) override
Definition: visualize.cpp:1043
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1563
bool const m_rubberband
Definition: visualize.h:136
bool process_all_types(VisualNode *node, bool this_will_be_displayed)
Definition: visualize.cpp:1067
MusicPlayer * gPlayer
Definition: musicplayer.cpp:35
#define PIANO_RMS_NEGLIGIBLE
Definition: visualize.h:243
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1546
void resize(const QSize &size) override
Definition: visualize.cpp:1391
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
goertzel_data max_magnitude_seen
Definition: visualize.h:250
bool process(VisualNode *node) override
Definition: visualize.cpp:152
long m_offset
Definition: videovisual.h:36
QFont GetMediumFont(void)
uint getImageCount()
virtual const QString & name(void) const =0
float sq(float a)
Definition: mythmiscutil.h:57
#define PIANO_N
Definition: visualize.h:238
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1455
uint plugins(QStringList *list) const override
Definition: visualize.cpp:524
AlbumArtImages * getAlbumArtImages(void)
#define PIANO_SPECTRUM_SMOOTHING
Definition: visualize.h:244
void setMax(int maxscale, int maxrange)
void zero_analysis(void)
Definition: visualize.cpp:943
goertzel_data coeff
Definition: visualize.h:249
unsigned int uint
Definition: compat.h:140
bool process(VisualNode *node=nullptr) override
Definition: visualize.cpp:1396
virtual ~VisualBase(void)
Definition: visualize.cpp:51
void resize(const QSize &size) override
Definition: visualize.cpp:142
QSize m_size
Definition: visualize.h:339
static guint32 * tmp
Definition: goom_core.c:35
vector< double > m_magnitude
Definition: visualize.h:295
short * m_right
Definition: videovisual.h:34
PianoFactory PianoFactory
int m_s
Definition: visualize.h:166
QColor m_blackTargetColor
Definition: visualize.h:285
unsigned char r
Definition: ParseText.cpp:329
bool draw(QPainter *p, const QColor &back) override
Definition: visualize.cpp:269
goertzel_data q1
Definition: visualize.h:249
ImageType m_imageType
Definition: musicmetadata.h:49
unsigned char b
Definition: ParseText.cpp:329
#define PIANO_KEYPRESS_TOO_LIGHT
Definition: visualize.h:246
bool m_xscreensaverenable
Definition: visualize.h:98
bool needsUpdate(void)
Definition: visualize.cpp:1435
#define PIANO_MIN_VOL
Definition: visualize.h:245
static VisFactory * g_pVisFactories
Definition: visualize.h:112
QSize m_size
Definition: visualize.h:135
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1330
int samples_process_before_display_update
Definition: visualize.h:256
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:556
QColor m_startColor
Definition: visualize.h:132
int * m_indices
Definition: visualize.h:165
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1517
QColor m_targetColor
Definition: visualize.h:133
void DoDisableScreensaver(void)
bool process(VisualNode *node) override
Definition: visualize.cpp:369
unsigned char t
Definition: ParseText.cpp:329
virtual ~Piano()
Definition: visualize.cpp:935
LogScale(int maxscale=0, int maxrange=0)
QColor m_whiteStartColor
Definition: visualize.h:282
QString getAlbumArtFile(void)
const QString & name(void) const override
Definition: visualize.cpp:543
vector< double > m_magnitudes
Definition: visualize.h:134
void DoRestoreScreensaver(void)
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1324
def log(debug, txt)
Definition: utilities.py:5
ImageType m_currImageType
Definition: visualize.h:318
int m_r
Definition: visualize.h:167
MusicMetadata * m_currentMetadata
Definition: visualize.h:321
uint plugins(QStringList *list) const override
Definition: visualize.cpp:550
VisualBase(bool screensaverenable=false)
Definition: visualize.cpp:44
const char * name
Definition: ParseText.cpp:328
MonoScopeFactory MonoScopeFactory
double const m_falloff
Definition: visualize.h:137
QSize m_cursize
Definition: visualize.h:317
const QString & name(void) const override
Definition: visualize.cpp:517
void * av_malloc(unsigned int size)
MythUIHelper * GetMythUI()
float clamp(float val, float minimum, float maximum)
Definition: mythmiscutil.h:41
const QString & name(void) const override
Definition: visualize.cpp:1510
goertzel_data q2
Definition: visualize.h:249
#define FFTW_N
Definition: visualize.cpp:38
void resize(const QSize &size) override
Definition: visualize.cpp:960
AlbumArtImage * getImageAt(uint index)
void drawWarning(QPainter *p, const QColor &back, const QSize &size, const QString &warning, int fontsize=28)
Definition: visualize.cpp:63
#define M_PI
Definition: goom_tools.h:5
const QString & name(void) const override
Definition: visualize.cpp:1556
void resize(const QSize &size) override
Definition: visualize.cpp:1535
QDateTime m_lastCycle
Definition: visualize.h:322
bool SaveAs(QByteArray &data)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define PIANO_AUDIO_SIZE
Definition: visualize.h:237
AlbumArtImage * getImage(ImageType type)
#define ALBUMARTCYCLETIME
this is the time an image is shown in the albumart visualizer
Definition: visualize.cpp:1433
bool process(VisualNode *node) override
Definition: visualize.cpp:1059
long m_length
Definition: videovisual.h:35
bool process(VisualNode *node=nullptr) override
Definition: visualize.cpp:1541
vector< QRect > m_rects
Definition: visualize.h:287
bool cycleImage(void)
Definition: visualize.cpp:1362
goertzel_data magnitude
Definition: visualize.h:249
BlankFactory BlankFactory
QColor m_blackStartColor
Definition: visualize.h:284
double clamp(double cur, double max, double min)
Definition: visualize.cpp:1195
void findFrontCover(void)
Definition: visualize.cpp:1343
void handleKeyPress(const QString &action) override
Definition: visualize.cpp:1401
AlbumArtFactory AlbumArtFactory
ImageType
Definition: musicmetadata.h:26
#define piano_audio
Definition: visualize.h:240
void av_free(void *ptr)
QSize m_size
Definition: visualize.h:288
virtual uint plugins(QStringList *list) const =0
StereoScopeFactory StereoScopeFactory
QColor m_whiteTargetColor
Definition: visualize.h:283
static guint32 * back
Definition: goom_core.c:34
AlbumArt(void)
Definition: visualize.cpp:1336
bool draw(QPainter *p, const QColor &back) override
Definition: visualize.cpp:459
virtual VisualBase * create(MainVisual *parent, const QString &pluginName) const =0
QSize m_size
Definition: visualize.h:316
const QString & name(void) const override
Definition: visualize.cpp:1317
#define SAMPLES_DEFAULT_SIZE
Definition: visualize.h:46
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1569
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1523
int m_fps
Definition: visualize.h:97
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1204
short * m_left
Definition: videovisual.h:33
piano_key_data * m_piano_data
Definition: visualize.h:292
unsigned long m_offset_processed
Definition: visualize.h:290
QImage m_image
Definition: visualize.h:319
int range() const
#define goertzel_data
Definition: visualize.h:241
bool processUndisplayed(VisualNode *node) override
Definition: visualize.cpp:1053
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:530
unsigned char g
Definition: ParseText.cpp:329