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