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