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