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