MythTV  master
visualize.cpp
Go to the documentation of this file.
1 /*
2  visualize.cpp
3 
4  (c) 2003 Thor Sigvaldason and Isaac Richards
5  VERY closely based on code from mq3 by Brad Hughes
6 
7  Part of the mythTV project
8 
9  music visualizers
10 */
11 
12 // C++
13 #include <cmath>
14 #include <iostream>
15 
16 // Qt
17 #include <QApplication>
18 #include <QCoreApplication>
19 #include <QImage>
20 #include <QPainter>
21 #include <QDir>
22 
23 // MythTV
24 #include <libmyth/mythcontext.h>
25 #include <libmythbase/mythdbcon.h>
26 #include <libmythbase/remotefile.h>
28 #include <libmythbase/mythdirs.h>
31 #include <libmythui/mythuihelper.h>
32 
33 // mythmusic
34 #include "decoder.h"
35 #include "inlines.h"
36 #include "mainvisual.h"
37 #include "musicplayer.h"
38 #include "visualize.h"
39 
40 static constexpr int FFTW_N { 512 };
41 // static_assert(FFTW_N==SAMPLES_DEFAULT_SIZE)
42 
43 
45 
46 VisualBase::VisualBase(bool screensaverenable)
47  : m_xscreensaverenable(screensaverenable)
48 {
51 }
52 
54 {
55  //
56  // This is only here so
57  // that derived classes
58  // can destruct properly
59  //
62 }
63 
64 
65 void VisualBase::drawWarning(QPainter *p, const QColor &back, const QSize size, const QString& warning, int fontSize)
66 {
67  p->fillRect(0, 0, size.width(), size.height(), back);
68  p->setPen(Qt::white);
69 
70  // Taken from removed MythUIHelper::GetMediumFont
71  QFont font = QApplication::font();
72 
73 #ifdef _WIN32
74  // logicalDpiY not supported in Windows.
75  int logicalDpiY = 100;
76  HDC hdc = GetDC(nullptr);
77  if (hdc)
78  {
79  logicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
80  ReleaseDC(nullptr, hdc);
81  }
82 #else
83  int logicalDpiY = GetMythMainWindow()->logicalDpiY();
84 #endif
85 
86  // adjust for screen resolution relative to 100 dpi
87  float floatSize = (16 * 100.0F) / logicalDpiY;
88  // adjust for myth GUI size relative to 800x600
89  float dummy = 0.0;
90  float hmult = 0.0;
91  GetMythMainWindow()->GetScalingFactors(hmult, dummy);
92  floatSize = floatSize * hmult;
93  // round and set
94  font.setPointSize(lroundf(floatSize));
95  font.setWeight(QFont::Bold);
96  font.setPointSizeF(fontSize * (size.width() / 800.0));
97  p->setFont(font);
98 
99  p->drawText(0, 0, size.width(), size.height(), Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextWordWrap, warning);
100 }
101 
103 // LogScale
104 
105 LogScale::LogScale(int maxscale, int maxrange)
106 {
107  setMax(maxscale, maxrange);
108 }
109 
111 {
112  delete [] m_indices;
113 }
114 
115 void LogScale::setMax(int maxscale, int maxrange)
116 {
117  if (maxscale == 0 || maxrange == 0)
118  return;
119 
120  m_s = maxscale;
121  m_r = maxrange;
122 
123  delete [] m_indices;
124 
125  auto domain = (long double) maxscale;
126  auto range = (long double) maxrange;
127  long double x = 1.0;
128  long double dx = 1.0;
129  long double e4 = 1.0E-8;
130 
131  m_indices = new int[maxrange];
132  for (int i = 0; i < maxrange; i++)
133  m_indices[i] = 0;
134 
135  // initialize log scale
136  for (uint i=0; i<10000 && (std::abs(dx) > e4); i++)
137  {
138  double t = std::log((domain + x) / x);
139  double y = (x * t) - range;
140  double yy = t - (domain / (x + domain));
141  dx = y / yy;
142  x -= dx;
143  }
144 
145  double alpha = x;
146  for (int i = 1; i < (int) domain; i++)
147  {
148  int scaled = (int) floor(0.5 + (alpha * log((double(i) + alpha) / alpha)));
149  if (scaled < 1)
150  scaled = 1;
151  if (m_indices[scaled - 1] < i)
152  m_indices[scaled - 1] = i;
153  }
154 }
155 
156 int LogScale::operator[](int index)
157 {
158  return m_indices[index];
159 }
160 
162 // StereoScope
163 
165 {
166  m_fps = 45;
167 }
168 
169 void StereoScope::resize( const QSize &newsize )
170 {
171  m_size = newsize;
172 
173  auto os = m_magnitudes.size();
174  m_magnitudes.resize( m_size.width() * 2_UZ );
175  for ( ; os < m_magnitudes.size(); os++ )
176  m_magnitudes[os] = 0.0;
177 }
178 
180 {
181  bool allZero = true;
182 
183 
184  if (node)
185  {
186  double index = 0;
187  double const step = (double)SAMPLES_DEFAULT_SIZE / m_size.width();
188  for ( int i = 0; i < m_size.width(); i++)
189  {
190  auto indexTo = (unsigned long)(index + step);
191  if (indexTo == (unsigned long)(index))
192  indexTo = (unsigned long)(index + 1);
193 
194  double valL = 0;
195  double valR = 0;
196 #if RUBBERBAND
197  if ( m_rubberband ) {
198  valL = m_magnitudes[ i ];
199  valR = m_magnitudes[ i + m_size.width() ];
200  if (valL < 0.) {
201  valL += m_falloff;
202  if ( valL > 0. )
203  valL = 0.;
204  }
205  else
206  {
207  valL -= m_falloff;
208  if ( valL < 0. )
209  valL = 0.;
210  }
211  if (valR < 0.)
212  {
213  valR += m_falloff;
214  if ( valR > 0. )
215  valR = 0.;
216  }
217  else
218  {
219  valR -= m_falloff;
220  if ( valR < 0. )
221  valR = 0.;
222  }
223  }
224 #endif
225  for (auto s = (unsigned long)index; s < indexTo && s < node->m_length; s++)
226  {
227  double adjHeight = static_cast<double>(m_size.height()) / 4.0;
228  double tmpL = ( ( node->m_left ? static_cast<double>(node->m_left[s]) : 0.) *
229  adjHeight ) / 32768.0;
230  double tmpR = ( ( node->m_right ? static_cast<double>(node->m_right[s]) : 0.) *
231  adjHeight ) / 32768.0;
232  if (tmpL > 0)
233  valL = (tmpL > valL) ? tmpL : valL;
234  else
235  valL = (tmpL < valL) ? tmpL : valL;
236  if (tmpR > 0)
237  valR = (tmpR > valR) ? tmpR : valR;
238  else
239  valR = (tmpR < valR) ? tmpR : valR;
240  }
241 
242  if (valL != 0. || valR != 0.)
243  allZero = false;
244 
245  m_magnitudes[ i ] = valL;
246  m_magnitudes[ i + m_size.width() ] = valR;
247 
248  index = index + step;
249  }
250 #if RUBBERBAND
251  }
252  else if (m_rubberband)
253  {
254  for ( int i = 0; i < m_size.width(); i++)
255  {
256  double valL = m_magnitudes[ i ];
257  if (valL < 0) {
258  valL += 2;
259  if (valL > 0.)
260  valL = 0.;
261  } else {
262  valL -= 2;
263  if (valL < 0.)
264  valL = 0.;
265  }
266 
267  double valR = m_magnitudes[ i + m_size.width() ];
268  if (valR < 0.) {
269  valR += m_falloff;
270  if (valR > 0.)
271  valR = 0.;
272  }
273  else
274  {
275  valR -= m_falloff;
276  if (valR < 0.)
277  valR = 0.;
278  }
279 
280  if (valL != 0. || valR != 0.)
281  allZero = false;
282 
283  m_magnitudes[ i ] = valL;
284  m_magnitudes[ i + m_size.width() ] = valR;
285  }
286 #endif
287  }
288  else
289  {
290  for ( int i = 0; (unsigned) i < m_magnitudes.size(); i++ )
291  m_magnitudes[ i ] = 0.;
292  }
293 
294  return allZero;
295 }
296 
297 bool StereoScope::draw( QPainter *p, const QColor &back )
298 {
299  if (back != Qt::green) // hack!!! for WaveForm
300  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
301  for ( int i = 1; i < m_size.width(); i++ )
302  {
303 #if TWOCOLOUR
304  // left
305  double 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  double r = m_startColor.red() +
315  (m_targetColor.red() - m_startColor.red()) * (per * per);
316  double g = m_startColor.green() +
317  (m_targetColor.green() - m_startColor.green()) * (per * per);
318  double b = m_startColor.blue() +
319  (m_targetColor.blue() - 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])
437  : static_cast<double>(node->m_left[s])) *
438  ( static_cast<double>(m_size.height()) / 2.0 ) ) / 65536.0;
439  if (tmp > 0)
440  {
441  val = (tmp > val) ? tmp : val;
442  }
443  else
444  {
445  val = (tmp < val) ? tmp : val;
446  }
447  }
448 
449  if ( val != 0. )
450  {
451  allZero = false;
452  }
453  m_magnitudes[ i ] = val;
454  index = index + step;
455  }
456  }
457 #if RUBBERBAND
458  else if (m_rubberband)
459  {
460  for (int i = 0; i < m_size.width(); i++) {
461  double val = m_magnitudes[ i ];
462  if (val < 0) {
463  val += 2;
464  if (val > 0.)
465  val = 0.;
466  } else {
467  val -= 2;
468  if (val < 0.)
469  val = 0.;
470  }
471 
472  if ( val != 0. )
473  allZero = false;
474  m_magnitudes[ i ] = val;
475  }
476  }
477 #endif
478  else
479  {
480  for (int i = 0; i < m_size.width(); i++ )
481  m_magnitudes[ i ] = 0.;
482  }
483 
484  return allZero;
485 }
486 
487 bool MonoScope::draw( QPainter *p, const QColor &back )
488 {
489  if (back != Qt::green) // hack!!! for WaveForm
490  p->fillRect( 0, 0, m_size.width(), m_size.height(), back );
491  for ( int i = 1; i < m_size.width(); i++ ) {
492 #if TWOCOLOUR
493  double per = ( static_cast<double>(m_magnitudes[i]) * 2.0 ) /
494  ( static_cast<double>(m_size.height()) / 4.0 );
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  double r = m_startColor.red() +
503  (m_targetColor.red() - m_startColor.red()) * (per * per);
504  double g = m_startColor.green() +
505  (m_targetColor.green() - m_startColor.green()) * (per * per);
506  double b = m_startColor.blue() +
507  (m_targetColor.blue() - 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 // WaveForm - see whole track - by twitham@sbcglobal.net, 2023/01
540 
542 {
543  saveload(nullptr);
544 }
545 
546 // cache current track, if any, before loading cache of given track
548 {
549  QString cache = GetConfDir() + "/MythMusic/WaveForm";
550  QString filename;
552  if (m_currentMetadata) // cache work in progress for next time
553  {
554  QDir dir(cache);
555  if (!dir.exists())
556  {
557  dir.mkpath(cache);
558  }
559  filename = QString("%1/%2.png").arg(cache)
560  .arg(stream ? 0 : m_currentMetadata->ID());
561  LOG(VB_PLAYBACK, LOG_INFO, QString("WF saving to %1").arg(filename));
562  if (!m_image.save(filename))
563  {
564  LOG(VB_GENERAL, LOG_ERR,
565  QString("WF saving to %1 failed: " + ENO).arg(filename));
566  }
567  }
568  m_currentMetadata = meta;
569  if (meta) // load previous work from cache
570  {
571  filename = QString("%1/%2.png").arg(cache).arg(stream ? 0 : meta->ID());
572  LOG(VB_PLAYBACK, LOG_INFO, QString("WF loading from %1").arg(filename));
573  if (!m_image.load(filename))
574  {
575  LOG(VB_GENERAL, LOG_WARNING,
576  QString("WF loading %1 failed, recreating").arg(filename));
577  }
578  // 60 seconds skips pixels with < 44100 streams like 22050,
579  // but this is now compensated for by drawing wider "pixels"
580  m_duration = stream ? 60000 : meta->Length().count(); // millisecs
581  }
582  if (m_image.isNull())
583  {
584  m_image = QImage(WF_WIDTH, WF_HEIGHT, QImage::Format_RGB32);
585  m_image.fill(qRgb(0, 0, 0));
586  }
587  m_minl = 0; // drop last pixel, prepare for first
588  m_maxl = 0;
589  m_sqrl = 0;
590  m_minr = 0;
591  m_maxr = 0;
592  m_sqrr = 0;
593  m_position = 0;
594  m_lastx = WF_WIDTH;
595  m_font = QApplication::font();
596 // m_font.setPointSize(14);
597  m_font.setPixelSize(20); // small to be mostly unnoticed
598 }
599 
600 unsigned long WaveForm::getDesiredSamples(void)
601 {
602  return (unsigned long) WF_AUDIO_SIZE; // Maximum we can be given
603 }
604 
606 {
607  // In 2023/01, v32 had a bug in mainvisual.cpp that this never
608  // received any nodes. Once I fixed that:
609 
610  // < m_vis->processUndisplayed(node);
611  // > m_vis->processUndisplayed(m_nodes.first());
612 
613  // now this receives *all* nodes.
614 
615  return process_all_types(node, false);
616 }
617 
619 {
620  // After 2023/01 bugfix above, processUndisplayed processes this
621  // node again after this process! If that is ever changed in
622  // mainvisual.cpp, then this might need adjusted. To test,
623  // uncomment the following line and see:
624 
625  // mythfrontend --loglevel debug -v playback
626 
627  // this would double-process the displayed nodes:
628  // return process_all_types(node, true);
629 
630  // StereoScope overlay must process only the displayed nodes
631  return StereoScope::process(node);
632 }
633 
634 bool WaveForm::process_all_types(VisualNode *node, bool displayed)
635 {
637  if (meta && meta != m_currentMetadata)
638  {
639  saveload(meta);
640  }
641  if (node && !m_image.isNull())
642  {
643  m_offset = node->m_offset.count() % m_duration; // for ::draw below
644  m_right = node->m_right;
645  uint n = node->m_length;
646  LOG(VB_PLAYBACK, LOG_DEBUG,
647  QString("WF process %1 samples at %2, display=%3").
648  arg(n).arg(m_offset).arg(displayed));
649 
650 // TODO: interpolate timestamps to process correct samples per pixel
651 // rather than fitting all we get in 1 or more pixels
652 
653  for (uint i = 0; i < n; i++) // find min/max and sum of squares
654  {
655  short int val = node->m_left[i];
656  if (val > m_maxl) m_maxl = val;
657  if (val < m_minl) m_minl = val;
658  m_sqrl += val * val;
659  if (m_right)
660  {
661  val = node->m_right[i];
662  if (val > m_maxr) m_maxr = val;
663  if (val < m_minr) m_minr = val;
664  m_sqrr += val * val;
665  }
666  m_position++;
667  }
668  uint xx = WF_WIDTH * m_offset / m_duration;
669  if (xx != m_lastx) // draw one finished line of min/max/rms
670  {
671  if (m_lastx > xx - 1) // right to left wrap
672  {
673  m_lastx = xx - 1;
674  }
675  int h = WF_HEIGHT / 4; // amplitude above or below zero
676  int y = WF_HEIGHT / 4; // left zero line
677  int yr = WF_HEIGHT * 3 / 4; // right zero line
678  if (!m_right)
679  { // mono - drop full waveform below StereoScope
680  y = yr;
681  }
682  // This "loop" runs only once except for short tracks or
683  // low sample rates that need some of the virtual "lines"
684  // to be drawn wider with more actual lines. I'd rather
685  // duplicate the vertical lines than draw rectangles since
686  // lines are the more common case. -twitham
687 
688  QPainter painter(&m_image);
689  for (uint x = m_lastx + 1; x <= xx; x++)
690  {
691  LOG(VB_PLAYBACK, LOG_DEBUG,
692  QString("WF painting at %1,%2/%3").arg(x).arg(y).arg(yr));
693 
694  painter.setPen(qRgb(0, 0, 0)); // clear prior content
695  painter.drawLine(x, 0, x, WF_HEIGHT);
696 
697  // Audacity uses 50,50,200 and 100,100,220 - I'm going
698  // darker to better contrast the StereoScope overlay
699  painter.setPen(qRgb(25, 25, 150)); // peak-to-peak
700  painter.drawLine(x, y - h * m_maxl / 32768,
701  x, y - h * m_minl / 32768);
702  if (m_right)
703  {
704  painter.drawLine(x, yr - h * m_maxr / 32768,
705  x, yr - h * m_minr / 32768);
706  }
707  painter.setPen(qRgb(150, 25, 25)); // RMS
708  int rmsl = sqrt(m_sqrl / m_position) * y / 32768;
709  painter.drawLine(x, y - rmsl, x, y + rmsl);
710  if (m_right)
711  {
712  int rmsr = sqrt(m_sqrr / m_position) * y / 32768;
713  painter.drawLine(x, yr - rmsr, x, yr + rmsr);
714  painter.drawLine(x, WF_HEIGHT / 2, // left/right delta
715  x, WF_HEIGHT / 2 - rmsl + rmsr);
716  }
717  }
718  m_minl = 0; // reset metrics for next line
719  m_maxl = 0;
720  m_sqrl = 0;
721  m_minr = 0;
722  m_maxr = 0;
723  m_sqrr = 0;
724  m_position = 0;
725  m_lastx = xx;
726  }
727  }
728  return false;
729 }
730 
731 bool WaveForm::draw( QPainter *p, const QColor &back )
732 {
733  p->fillRect(0, 0, 0, 0, back); // no clearing, here to suppress warning
734  if (!m_image.isNull())
735  { // background, updated by ::process above
736  p->drawImage(0, 0, m_image.scaled(m_size,
737  Qt::IgnoreAspectRatio,
738  Qt::SmoothTransformation));
739  }
740 
741  StereoScope::draw(p, Qt::green); // green == no clearing!
742 
743  p->setPen(Qt::yellow);
744  unsigned int x = m_size.width() * m_offset / m_duration;
745  p->drawLine(x, 0, x, m_size.height());
746 
747  if (m_showtext && m_size.width() > 500) // metrics in corners
748  {
749  p->setPen(Qt::white);
750  p->setFont(m_font);
751  QRect text(5, 5, m_size.width() - 10, m_size.height() - 10);
752  p->drawText(text, Qt::AlignTop | Qt::AlignLeft,
753  QString("%1:%2")
754  .arg(m_offset / 1000 / 60)
755  .arg(m_offset / 1000 % 60, 2, 10, QChar('0')));
756  p->drawText(text, Qt::AlignTop | Qt::AlignRight,
757  QString("%1:%2")
758  .arg(m_duration / 1000 / 60)
759  .arg(m_duration / 1000 % 60, 2, 10, QChar('0')));
760  p->drawText(text, Qt::AlignBottom | Qt::AlignLeft,
761  QString("%1 lines/s")
762  .arg(1000.0 * WF_WIDTH / m_duration, 0, 'f', 1));
763  p->drawText(text, Qt::AlignBottom | Qt::AlignRight,
764  QString("%1 ms/line")
765  .arg(1.0 * m_duration / WF_WIDTH, 0, 'f', 1));
766  }
767  return true;
768 }
769 
770 void WaveForm::handleKeyPress(const QString &action)
771 {
772  LOG(VB_PLAYBACK, LOG_INFO, QString("WF keypress = %1").arg(action));
773 
774  // I'd like to toggle overlay text upon certain key hit, but
775  // mythfrontend doesn't appear to call this. Bug?
776  if (action == "SELECT")
777  {
779  }
780 }
781 
783 // StereoScopeFactory
784 
785 static class StereoScopeFactory : public VisFactory
786 {
787  public:
788  const QString &name(void) const override // VisFactory
789  {
790  static QString s_name = QCoreApplication::translate("Visualizers",
791  "StereoScope");
792  return s_name;
793  }
794 
795  uint plugins(QStringList *list) const override // VisFactory
796  {
797  *list << name();
798  return 1;
799  }
800 
801  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
802  {
803  return new StereoScope();
804  }
806 
807 
809 // MonoScopeFactory
810 
811 static class MonoScopeFactory : public VisFactory
812 {
813  public:
814  const QString &name(void) const override // VisFactory
815  {
816  static QString s_name = QCoreApplication::translate("Visualizers",
817  "MonoScope");
818  return s_name;
819  }
820 
821  uint plugins(QStringList *list) const override // VisFactory
822  {
823  *list << name();
824  return 1;
825  }
826 
827  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
828  {
829  return new MonoScope();
830  }
832 
834 // WaveFormFactory
835 
836 static class WaveFormFactory : public VisFactory
837 {
838  public:
839  const QString &name(void) const override // VisFactory
840  {
841  static QString s_name = QCoreApplication::translate("Visualizers",
842  "WaveForm");
843  return s_name;
844  }
845 
846  uint plugins(QStringList *list) const override // VisFactory
847  {
848  *list << name();
849  return 1;
850  }
851 
853  MainVisual */*parent*/, const QString &/*pluginName*/) const override
854  {
855  return new WaveForm();
856  }
858 
860 // Spectrum
861 //
862 
864 {
865  LOG(VB_GENERAL, LOG_INFO, QString("Spectrum : Being Initialised"));
866 
867  m_fps = 15;
868 
869  m_dftL = static_cast<FFTComplex*>(av_malloc(sizeof(FFTComplex) * FFTW_N));
870  m_dftR = static_cast<FFTComplex*>(av_malloc(sizeof(FFTComplex) * FFTW_N));
871 
872  m_fftContextForward = av_fft_init(std::log2(FFTW_N), 0);
873 }
874 
876 {
877  av_freep(&m_dftL);
878  av_freep(&m_dftR);
879  av_fft_end(m_fftContextForward);
880 }
881 
882 void Spectrum::resize(const QSize &newsize)
883 {
884  // Just change internal data about the
885  // size of the pixmap to be drawn (ie. the
886  // size of the screen) and the logically
887  // ensuing number of up/down bars to hold
888  // the audio magnitudes
889 
890  m_size = newsize;
891 
892  m_analyzerBarWidth = m_size.width() / 64;
893 
894  if (m_analyzerBarWidth < 6)
895  m_analyzerBarWidth = 6;
896 
897  m_scale.setMax(192, m_size.width() / m_analyzerBarWidth);
898 
899  m_rects.resize( m_scale.range() );
900  int w = 0;
901  // NOLINTNEXTLINE(modernize-loop-convert)
902  for (uint i = 0; i < (uint)m_rects.size(); i++, w += m_analyzerBarWidth)
903  {
904  m_rects[i].setRect(w, m_size.height() / 2, m_analyzerBarWidth - 1, 1);
905  }
906 
907  m_magnitudes.resize( m_scale.range() * 2 );
908  // NOLINTNEXTLINE(modernize-loop-convert)
909  for (uint os = m_magnitudes.size(); os < (uint)m_magnitudes.size(); os++)
910  {
911  m_magnitudes[os] = 0.0;
912  }
913 
914  m_scaleFactor = ( static_cast<double>(m_size.height()) / 2.0 ) /
915  log( static_cast<double>(FFTW_N) );
916 }
917 
918 template<typename T> T sq(T a) { return a*a; };
919 
921 {
922  // Take a bunch of data in *node
923  // and break it down into spectrum
924  // values
925  bool allZero = true;
926 
927  uint i = 0;
928  long w = 0;
929  QRect *rectsp = m_rects.data();
930  double *magnitudesp = m_magnitudes.data();
931 
932  if (node)
933  {
934  i = node->m_length;
935  if (i > FFTW_N)
936  i = FFTW_N;
937  for (unsigned long k = 0; k < node->m_length; k++)
938  {
939  m_dftL[k] = (FFTComplex){ .re = (FFTSample)node->m_left[k], .im = 0 };
940  if (node->m_right)
941  m_dftR[k] = (FFTComplex){ .re = (FFTSample)node->m_right[k], .im = 0 };
942  }
943  }
944 
945  for (auto k = i; k < FFTW_N; k++)
946  {
947  m_dftL[k] = (FFTComplex){ .re = 0, .im = 0 };
948  m_dftL[k] = (FFTComplex){ .re = 0, .im = 0 };
949  }
950  av_fft_permute(m_fftContextForward, m_dftL);
951  av_fft_calc(m_fftContextForward, m_dftL);
952 
953  av_fft_permute(m_fftContextForward, m_dftR);
954  av_fft_calc(m_fftContextForward, m_dftR);
955 
956  long index = 1;
957 
958  for (i = 0; (int)i < m_rects.size(); i++, w += m_analyzerBarWidth)
959  {
960  // The 1D output is Hermitian symmetric (Yk = Yn-k) so Yn = Y0 etc.
961  // The dft_r2c_1d plan doesn't output these redundant values
962  // and furthermore they're not allocated in the ctor
963  double tmp = 2 * sq(m_dftL[index].re);
964  double magL = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
965 
966  tmp = 2 * sq(m_dftR[index].re);
967  double magR = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
968 
969  double adjHeight = static_cast<double>(m_size.height()) / 2.0;
970  if (magL > adjHeight)
971  {
972  magL = adjHeight;
973  }
974  if (magL < magnitudesp[i])
975  {
976  tmp = magnitudesp[i] - m_falloff;
977  if ( tmp < magL )
978  {
979  tmp = magL;
980  }
981  magL = tmp;
982  }
983  if (magL < 1.)
984  {
985  magL = 1.;
986  }
987 
988  if (magR > adjHeight)
989  {
990  magR = adjHeight;
991  }
992  if (magR < magnitudesp[i + m_scale.range()])
993  {
994  tmp = magnitudesp[i + m_scale.range()] - m_falloff;
995  if ( tmp < magR )
996  {
997  tmp = magR;
998  }
999  magR = tmp;
1000  }
1001  if (magR < 1.)
1002  {
1003  magR = 1.;
1004  }
1005 
1006  if (magR != 1 || magL != 1)
1007  {
1008  allZero = false;
1009  }
1010 
1011  magnitudesp[i] = magL;
1012  magnitudesp[i + m_scale.range()] = magR;
1013  rectsp[i].setTop( m_size.height() / 2 - int( magL ) );
1014  rectsp[i].setBottom( m_size.height() / 2 + int( magR ) );
1015 
1016  index = m_scale[i];
1017  }
1018 
1019  Q_UNUSED(allZero);
1020  return false;
1021 }
1022 
1023 double Spectrum::clamp(double cur, double max, double min)
1024 {
1025  if (cur > max)
1026  cur = max;
1027  if (cur < min)
1028  cur = min;
1029  return cur;
1030 }
1031 
1032 bool Spectrum::draw(QPainter *p, const QColor &back)
1033 {
1034  // This draws on a pixmap owned by MainVisual.
1035  //
1036  // In other words, this is not a Qt Widget, it
1037  // just uses some Qt methods to draw on a pixmap.
1038  // MainVisual then bitblts that onto the screen.
1039 
1040  QRect *rectsp = m_rects.data();
1041 
1042  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1043  for (uint i = 0; i < (uint)m_rects.size(); i++)
1044  {
1045  double per = double( rectsp[i].height() - 2 ) / double( m_size.height() );
1046 
1047  per = clamp(per, 1.0, 0.0);
1048 
1049  double r = m_startColor.red() +
1050  (m_targetColor.red() - m_startColor.red()) * (per * per);
1051  double g = m_startColor.green() +
1052  (m_targetColor.green() - m_startColor.green()) * (per * per);
1053  double b = m_startColor.blue() +
1054  (m_targetColor.blue() - m_startColor.blue()) * (per * per);
1055 
1056  r = clamp(r, 255.0, 0.0);
1057  g = clamp(g, 255.0, 0.0);
1058  b = clamp(b, 255.0, 0.0);
1059 
1060  if(rectsp[i].height() > 4)
1061  p->fillRect(rectsp[i], QColor(int(r), int(g), int(b)));
1062  }
1063 
1064  return true;
1065 }
1066 
1067 static class SpectrumFactory : public VisFactory
1068 {
1069  public:
1070  const QString &name(void) const override // VisFactory
1071  {
1072  static QString s_name = QCoreApplication::translate("Visualizers",
1073  "Spectrum");
1074  return s_name;
1075  }
1076 
1077  uint plugins(QStringList *list) const override // VisFactory
1078  {
1079  *list << name();
1080  return 1;
1081  }
1082 
1083  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1084  {
1085  return new Spectrum();
1086  }
1088 
1090 // Squares
1091 //
1092 
1094 {
1096 }
1097 
1098 void Squares::resize (const QSize &newsize) {
1099  // Trick the spectrum analyzer into calculating 16 rectangles
1101  // We have our own copy, Spectrum has it's own...
1102  m_actualSize = newsize;
1103 }
1104 
1105 void Squares::drawRect(QPainter *p, QRect *rect, int i, int c, int w, int h)
1106 {
1107  double per = NAN;
1108  int correction = (m_actualSize.width() % m_rects.size ()) / 2;
1109  int x = ((i / 2) * w) + correction;
1110  int y = 0;
1111 
1112  if (i % 2 == 0)
1113  {
1114  y = c - h;
1115  per = double(m_fakeHeight - rect->top()) / double(m_fakeHeight);
1116  }
1117  else
1118  {
1119  y = c;
1120  per = double(rect->bottom()) / double(m_fakeHeight);
1121  }
1122 
1123  per = clamp(per, 1.0, 0.0);
1124 
1125  double r = m_startColor.red() +
1126  (m_targetColor.red() - m_startColor.red()) * (per * per);
1127  double g = m_startColor.green() +
1128  (m_targetColor.green() - m_startColor.green()) * (per * per);
1129  double b = m_startColor.blue() +
1130  (m_targetColor.blue() - m_startColor.blue()) * (per * per);
1131 
1132  r = clamp(r, 255.0, 0.0);
1133  g = clamp(g, 255.0, 0.0);
1134  b = clamp(b, 255.0, 0.0);
1135 
1136  p->fillRect (x, y, w, h, QColor (int(r), int(g), int(b)));
1137 }
1138 
1139 bool Squares::draw(QPainter *p, const QColor &back)
1140 {
1141  p->fillRect (0, 0, m_actualSize.width(), m_actualSize.height(), back);
1142  int w = m_actualSize.width() / (m_rects.size() / 2);
1143  int h = w;
1144  int center = m_actualSize.height() / 2;
1145 
1146  QRect *rectsp = m_rects.data();
1147  for (uint i = 0; i < (uint)m_rects.size(); i++)
1148  drawRect(p, &(rectsp[i]), i, center, w, h);
1149 
1150  return true;
1151 }
1152 
1153 static class SquaresFactory : public VisFactory
1154 {
1155  public:
1156  const QString &name(void) const override // VisFactory
1157  {
1158  static QString s_name = QCoreApplication::translate("Visualizers",
1159  "Squares");
1160  return s_name;
1161  }
1162 
1163  uint plugins(QStringList *list) const override // VisFactory
1164  {
1165  *list << name();
1166  return 1;
1167  }
1168 
1169  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1170  {
1171  return new Squares();
1172  }
1174 
1175 
1177 {
1178  // Setup the "magical" audio coefficients
1179  // required by the Goetzel Algorithm
1180 
1181  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Initialised"));
1182 
1183  m_pianoData = (piano_key_data *) malloc(sizeof(piano_key_data) * kPianoNumKeys);
1184  m_audioData = (piano_audio *) malloc(sizeof(piano_audio) * kPianoAudioSize);
1185 
1186  double sample_rate = 44100.0; // TODO : This should be obtained from gPlayer (likely candidate...)
1187 
1188  m_fps = 20; // This is the display frequency. We're capturing all audio chunks by defining .process_undisplayed() though.
1189 
1190  double concert_A = 440.0;
1191  double semi_tone = pow(2.0, 1.0/12.0);
1192 
1193  /* Lowest note on piano is 4 octaves below concert A */
1194  double bottom_A = concert_A / 2.0 / 2.0 / 2.0 / 2.0;
1195 
1196  double current_freq = bottom_A;
1197  for (uint key = 0; key < kPianoNumKeys; key++)
1198  {
1199  // This is constant through time
1200  m_pianoData[key].coeff = (goertzel_data)(2.0 * cos(2.0 * M_PI * current_freq / sample_rate));
1201 
1202  // Want 20 whole cycles of the current waveform at least
1203  double samples_required = sample_rate/current_freq * 20.0;
1204  if (samples_required > sample_rate/4.0)
1205  {
1206  // For the really low notes, 4 updates a second is good enough...
1207  samples_required = sample_rate/4.0;
1208  }
1209  if (samples_required < sample_rate/(double)m_fps * 0.75)
1210  { // For the high notes, use as many samples as we need in a display_fps
1211  samples_required = sample_rate/(double)m_fps * 0.75;
1212  }
1213  m_pianoData[key].samples_process_before_display_update = (int)samples_required;
1214  m_pianoData[key].is_black_note = false; // Will be put right in .resize()
1215 
1216  current_freq *= semi_tone;
1217  }
1218 
1219  zero_analysis();
1220 }
1221 
1223 {
1224  if (m_pianoData)
1225  free(m_pianoData);
1226  if (m_audioData)
1227  free(m_audioData);
1228 }
1229 
1231 {
1232  for (uint key = 0; key < kPianoNumKeys; key++)
1233  {
1234  // These get updated continously, and must be stored between chunks of audio data
1235  m_pianoData[key].q2 = 0.0F;
1236  m_pianoData[key].q1 = 0.0F;
1237  m_pianoData[key].magnitude = 0.0F;
1239  (goertzel_data)(kPianoRmsNegligible * kPianoRmsNegligible); // This is a guess - will be quickly overwritten
1240 
1241  m_pianoData[key].samples_processed = 0;
1242  }
1243  m_offsetProcessed = 0ms;
1244 }
1245 
1246 void Piano::resize(const QSize &newsize)
1247 {
1248  // Just change internal data about the
1249  // size of the pixmap to be drawn (ie. the
1250  // size of the screen) and the logically
1251  // ensuing number of up/down bars to hold
1252  // the audio magnitudes
1253 
1254  m_size = newsize;
1255 
1256  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Being Resized"));
1257 
1258  zero_analysis();
1259 
1260  // There are 88-36=52 white notes on piano keyboard
1261  double key_unit_size = (double)m_size.width() / 54.0; // One white key extra spacing, if possible
1262  if (key_unit_size < 10.0) // Keys have to be at least this many pixels wide
1263  key_unit_size = 10.0;
1264 
1265  double white_width_pct = .8;
1266  double black_width_pct = .6;
1267  double black_offset_pct = .05;
1268 
1269  double white_height_pct = 6;
1270  double black_height_pct = 4;
1271 
1272  // This is the starting position of the keyboard (may be beyond LHS)
1273  // - actually position of C below bottom A (will be added to...). This is 4 octaves below middle C.
1274  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
1275  double top_of_keys = (double)m_size.height() / 2.0 - key_unit_size * white_height_pct / 2.0; // Vertically center keys
1276 
1277  m_rects.resize(kPianoNumKeys);
1278 
1279  for (uint key = 0; key < kPianoNumKeys; key++)
1280  {
1281  int note = ((int)key - 3 + 12) % 12; // This means that C=0, C#=1, D=2, etc (since lowest note is bottom A)
1282  if (note == 0) // If we're on a 'C', move the left 'cursor' over an octave
1283  {
1284  left += key_unit_size*7.0;
1285  }
1286 
1287  double center = 0.0;
1288  double offset = 0.0;
1289  bool is_black = false;
1290 
1291  switch (note)
1292  {
1293  case 0: center = 0.5; break;
1294  case 1: center = 1.0; is_black = true; offset = -1; break;
1295  case 2: center = 1.5; break;
1296  case 3: center = 2.0; is_black = true; offset = +1; break;
1297  case 4: center = 2.5; break;
1298  case 5: center = 3.5; break;
1299  case 6: center = 4.0; is_black = true; offset = -2; break;
1300  case 7: center = 4.5; break;
1301  case 8: center = 5.0; is_black = true; offset = 0; break;
1302  case 9: center = 5.5; break;
1303  case 10: center = 6.0; is_black = true; offset = 2; break;
1304  case 11: center = 6.5; break;
1305  }
1306  m_pianoData[key].is_black_note = is_black;
1307 
1308  double width = (is_black ? black_width_pct:white_width_pct) * key_unit_size;
1309  double height = (is_black? black_height_pct:white_height_pct) * key_unit_size;
1310 
1311  m_rects[key].setRect(
1312  left + center * key_unit_size // Basic position of left side of key
1313  - width / 2.0 // Less half the width
1314  + (is_black ? (offset * black_offset_pct * key_unit_size):0.0), // And jiggle the positions of the black keys for aethetic reasons
1315  top_of_keys, // top
1316  width, // width
1317  height // height
1318  );
1319  }
1320 
1321  m_magnitude.resize(kPianoNumKeys);
1322  for (double & key : m_magnitude)
1323  key = 0.0;
1324 }
1325 
1326 unsigned long Piano::getDesiredSamples(void)
1327 {
1328  // We want all the data! (within reason)
1329  // typical observed values are 882 -
1330  // 12.5 chunks of data per second from 44100Hz signal : Sampled at 50Hz, lots of 4, see :
1331  // mythtv/libs/libmyth/audio/audiooutputbase.cpp :: AudioOutputBase::AddData
1332  // See : mythtv/mythplugins/mythmusic/mythmusic/avfdecoder.cpp "20ms worth"
1333  return (unsigned long) kPianoAudioSize; // Maximum we can be given
1334 }
1335 
1337 {
1338  //LOG(VB_GENERAL, LOG_INFO, QString("Piano : Processing undisplayed node"));
1339  return process_all_types(node, false);
1340 }
1341 
1343 {
1344  //LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Processing node for DISPLAY"));
1345 
1346  // See WaveForm::process* above
1347  // return process_all_types(node, true);
1348 
1349  return false;
1350 }
1351 
1352 bool Piano::process_all_types(VisualNode *node, bool /*this_will_be_displayed*/)
1353 {
1354  // Take a bunch of data in *node and break it down into piano key spectrum values
1355  // NB: Remember the state data between calls, so as to accumulate more accurate results.
1356  bool allZero = true;
1357  uint n = 0;
1358 
1359  if (node)
1360  {
1361  piano_audio short_to_bounded = 32768.0F;
1362 
1363  // Detect start of new song (current node more than 10s earlier than already seen)
1364  if (node->m_offset + 10s < m_offsetProcessed)
1365  {
1366  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Node offset=%1 too far backwards : NEW SONG").arg(node->m_offset.count()));
1367  zero_analysis();
1368  }
1369 
1370  // Check whether we've seen this node (more recently than 10secs ago)
1371  if (node->m_offset <= m_offsetProcessed)
1372  {
1373  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Already seen node offset=%1, returning without processing").arg(node->m_offset.count()));
1374  return allZero; // Nothing to see here - the server can stop if it wants to
1375  }
1376 
1377  //LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Processing node offset=%1, size=%2").arg(node->m_offset).arg(node->m_length));
1378  n = node->m_length;
1379 
1380  if (node->m_right) // Preprocess the data into a combined middle channel, if we have stereo data
1381  {
1382  for (uint i = 0; i < n; i++)
1383  {
1384  m_audioData[i] = ((piano_audio)node->m_left[i] + (piano_audio)node->m_right[i]) / 2.0F / short_to_bounded;
1385  }
1386  }
1387  else // This is only one channel of data
1388  {
1389  for (uint i = 0; i < n; i++)
1390  {
1391  m_audioData[i] = (piano_audio)node->m_left[i] / short_to_bounded;
1392  }
1393  }
1394  }
1395  else
1396  {
1397  LOG(VB_GENERAL, LOG_DEBUG, QString("Hit an empty node, and returning empty-handed"));
1398  return allZero; // Nothing to see here - the server can stop if it wants to
1399  }
1400 
1401  for (uint key = 0; key < kPianoNumKeys; key++)
1402  {
1403  goertzel_data coeff = m_pianoData[key].coeff;
1404  goertzel_data q2 = m_pianoData[key].q2;
1405  goertzel_data q1 = m_pianoData[key].q1;
1406 
1407  for (uint i = 0; i < n; i++)
1408  {
1409  goertzel_data q0 = coeff * q1 - q2 + m_audioData[i];
1410  q2 = q1;
1411  q1 = q0;
1412  }
1413  m_pianoData[key].q2 = q2;
1414  m_pianoData[key].q1 = q1;
1415 
1416  m_pianoData[key].samples_processed += n;
1417 
1418  int n_samples = m_pianoData[key].samples_processed;
1419 
1420  // Only do this update if we've processed enough chunks for this key...
1421  if (n_samples > m_pianoData[key].samples_process_before_display_update)
1422  {
1423  goertzel_data magnitude2 = q1*q1 + q2*q2 - q1*q2*coeff;
1424 
1425 #if 0
1426  // This is RMS of signal
1427  goertzel_data magnitude_av =
1428  sqrt(magnitude2)/(goertzel_data)n_samples; // Should be 0<magnitude_av<.5
1429 #else
1430  // This is pure magnitude of signal
1431  goertzel_data magnitude_av =
1432  magnitude2/(goertzel_data)n_samples/(goertzel_data)n_samples; // Should be 0<magnitude_av<.25
1433 #endif
1434 
1435 #if 0
1436  // Take logs everywhere, and shift up to [0, ??]
1437  if(magnitude_av > 0.0F)
1438  {
1439  magnitude_av = log(magnitude_av);
1440  }
1441  else
1442  {
1443  magnitude_av = kPianoMinVol;
1444  }
1445  magnitude_av -= kPianoMinVol;
1446 
1447  if (magnitude_av < 0.0F)
1448  {
1449  magnitude_av = 0.0;
1450  }
1451 #endif
1452 
1453  if (magnitude_av > (goertzel_data)0.01)
1454  {
1455  allZero = false;
1456  }
1457 
1458  m_pianoData[key].magnitude = magnitude_av; // Store this for later : We'll do the colours from this...
1459  if ( m_pianoData[key].max_magnitude_seen < magnitude_av)
1460  {
1461  m_pianoData[key].max_magnitude_seen = magnitude_av;
1462  }
1463  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Updated Key %1 from %2 samples, magnitude=%3")
1464  .arg(key).arg(n_samples).arg(magnitude_av));
1465 
1466  m_pianoData[key].samples_processed = 0; // Reset the counts, now that we've set the magnitude...
1467  m_pianoData[key].q1 = (goertzel_data)0.0;
1468  m_pianoData[key].q2 = (goertzel_data)0.0;
1469  }
1470  }
1471 
1472  if (node)
1473  {
1474  // All done now - record that we've done this offset
1475  m_offsetProcessed = node->m_offset;
1476  }
1477 
1478  return allZero;
1479 }
1480 
1481 double Piano::clamp(double cur, double max, double min)
1482 {
1483  if (cur > max)
1484  cur = max;
1485  if (cur < min)
1486  cur = min;
1487  return cur;
1488 }
1489 
1490 bool Piano::draw(QPainter *p, const QColor &back)
1491 {
1492  // This draws on a pixmap owned by MainVisual.
1493  //
1494  // In other words, this is not a Qt Widget, it
1495  // just uses some Qt methods to draw on a pixmap.
1496  // MainVisual then bitblts that onto the screen.
1497 
1498  QRect *rectsp = (m_rects).data();
1499  double *magnitudep = (m_magnitude).data();
1500 
1501  unsigned int n = kPianoNumKeys;
1502 
1503  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1504 
1505  // Protect maximum array length
1506  if(n > (uint)m_rects.size())
1507  n = (uint)m_rects.size();
1508 
1509  // Sweep up across the keys, making sure the max_magnitude_seen is at minimum X% of its neighbours
1510  double mag = kPianoRmsNegligible;
1511  for (uint key = 0; key < n; key++)
1512  {
1513  if (m_pianoData[key].max_magnitude_seen < static_cast<float>(mag))
1514  {
1515  // Spread the previous value to this key
1516  m_pianoData[key].max_magnitude_seen = mag;
1517  }
1518  else
1519  {
1520  // This key has seen better peaks, use this for the next one
1521  mag = m_pianoData[key].max_magnitude_seen;
1522  }
1523  mag *= kPianoSpectrumSmoothing;
1524  }
1525 
1526  // Similarly, down, making sure the max_magnitude_seen is at minimum X% of its neighbours
1527  mag = kPianoRmsNegligible;
1528  for (int key_i = n - 1; key_i >= 0; key_i--)
1529  {
1530  uint key = key_i; // Wow, this is to avoid a zany error for ((unsigned)0)--
1531  if (m_pianoData[key].max_magnitude_seen < static_cast<float>(mag))
1532  {
1533  // Spread the previous value to this key
1534  m_pianoData[key].max_magnitude_seen = mag;
1535  }
1536  else
1537  {
1538  // This key has seen better peaks, use this for the next one
1539  mag = m_pianoData[key].max_magnitude_seen;
1540  }
1541  mag *= kPianoSpectrumSmoothing;
1542  }
1543 
1544  // Now find the key that has been hit the hardest relative to its experience, and renormalize...
1545  // Set a minimum, to prevent divide-by-zero (and also all-pressed when music very quiet)
1546  double magnitude_max = kPianoRmsNegligible;
1547  for (uint key = 0; key < n; key++)
1548  {
1550  if (magnitude_max < mag)
1551  magnitude_max = mag;
1552 
1553  magnitudep[key] = mag;
1554  }
1555 
1556  // Deal with all the white keys first
1557  for (uint key = 0; key < n; key++)
1558  {
1559  if (m_pianoData[key].is_black_note)
1560  continue;
1561 
1562  double per = magnitudep[key] / magnitude_max;
1563  per = clamp(per, 1.0, 0.0); // By construction, this should be unnecessary
1564 
1565  if (per < kPianoKeypressTooLight)
1566  per = 0.0; // Clamp to zero for lightly detected keys
1567  LOG(VB_GENERAL, LOG_DEBUG, QString("Piano : Display key %1, magnitude=%2, seen=%3")
1568  .arg(key).arg(per*100.0).arg(m_pianoData[key].max_magnitude_seen));
1569 
1570  double r = m_whiteStartColor.red() + (m_whiteTargetColor.red() - m_whiteStartColor.red()) * per;
1571  double g = m_whiteStartColor.green() + (m_whiteTargetColor.green() - m_whiteStartColor.green()) * per;
1572  double b = m_whiteStartColor.blue() + (m_whiteTargetColor.blue() - m_whiteStartColor.blue()) * per;
1573 
1574  p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
1575  }
1576 
1577  // Then overlay the black keys
1578  for (uint key = 0; key < n; key++)
1579  {
1580  if (!m_pianoData[key].is_black_note)
1581  continue;
1582 
1583  double per = magnitudep[key]/magnitude_max;
1584  per = clamp(per, 1.0, 0.0); // By construction, this should be unnecessary
1585 
1586  if (per < kPianoKeypressTooLight)
1587  per = 0.0; // Clamp to zero for lightly detected keys
1588 
1589  double r = m_blackStartColor.red() + (m_blackTargetColor.red() - m_blackStartColor.red()) * per;
1590  double g = m_blackStartColor.green() + (m_blackTargetColor.green() - m_blackStartColor.green()) * per;
1591  double b = m_blackStartColor.blue() + (m_blackTargetColor.blue() - m_blackStartColor.blue()) * per;
1592 
1593  p->fillRect(rectsp[key], QColor(int(r), int(g), int(b)));
1594  }
1595 
1596  return true;
1597 }
1598 
1599 static class PianoFactory : public VisFactory
1600 {
1601  public:
1602  const QString &name(void) const override // VisFactory
1603  {
1604  static QString s_name = QCoreApplication::translate("Visualizers",
1605  "Piano");
1606  return s_name;
1607  }
1608 
1609  uint plugins(QStringList *list) const override // VisFactory
1610  {
1611  *list << name();
1612  return 1;
1613  }
1614 
1615  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1616  {
1617  return new Piano();
1618  }
1619 }PianoFactory;
1620 
1622  m_lastCycle(QDateTime::currentDateTime())
1623 {
1624  findFrontCover();
1625  m_fps = 1;
1626 }
1627 
1629 {
1630  if (!gPlayer->getCurrentMetadata())
1631  return;
1632 
1633  // if a front cover image is available show that first
1635  if (albumArt->getImage(IT_FRONTCOVER))
1637  else
1638  {
1639  // not available so just show the first image available
1640  if (albumArt->getImageCount() > 0)
1641  m_currImageType = albumArt->getImageAt(0)->m_imageType;
1642  else
1644  }
1645 }
1646 
1648 {
1649  if (!gPlayer->getCurrentMetadata())
1650  return false;
1651 
1653  int newType = m_currImageType;
1654 
1655  // If we only have one image there is nothing to cycle
1656  if (albumArt->getImageCount() > 1)
1657  {
1658  do
1659  {
1660  newType++;
1661  if (newType == IT_LAST)
1662  newType = IT_UNKNOWN;
1663  } while (!albumArt->getImage((ImageType) newType));
1664  }
1665 
1666  if (newType != m_currImageType)
1667  {
1668  m_currImageType = (ImageType) newType;
1669  m_lastCycle = QDateTime::currentDateTime();
1670  return true;
1671  }
1672 
1673  return false;
1674 }
1675 
1676 void AlbumArt::resize(const QSize &newsize)
1677 {
1678  m_size = newsize;
1679 }
1680 
1682 {
1683  return false;
1684 }
1685 
1686 void AlbumArt::handleKeyPress(const QString &action)
1687 {
1688  if (action == "SELECT")
1689  {
1690  if (gPlayer->getCurrentMetadata())
1691  {
1693  int newType = m_currImageType;
1694 
1695  if (albumArt.getImageCount() > 0)
1696  {
1697  newType++;
1698 
1699  while (!albumArt.getImage((ImageType) newType))
1700  {
1701  newType++;
1702  if (newType == IT_LAST)
1703  newType = IT_UNKNOWN;
1704  }
1705  }
1706 
1707  if (newType != m_currImageType)
1708  {
1709  m_currImageType = (ImageType) newType;
1710  // force an update
1711  m_cursize = QSize(0, 0);
1712  }
1713  }
1714  }
1715 }
1716 
1718 static constexpr qint64 ALBUMARTCYCLETIME { 10 };
1719 
1721 {
1722  // if the track has changed we need to update the image
1724  {
1726  findFrontCover();
1727  return true;
1728  }
1729 
1730  // if it's time to cycle to the next image we need to update the image
1731  if (m_lastCycle.addSecs(ALBUMARTCYCLETIME) < QDateTime::currentDateTime())
1732  {
1733  if (cycleImage())
1734  return true;
1735  }
1736 
1737  return false;
1738 }
1739 
1740 bool AlbumArt::draw(QPainter *p, const QColor &back)
1741 {
1742  if (needsUpdate())
1743  {
1744  QImage art;
1745  QString imageFilename = gPlayer->getCurrentMetadata()->getAlbumArtFile(m_currImageType);
1746 
1747  if (imageFilename.startsWith("myth://"))
1748  {
1749  auto *rf = new RemoteFile(imageFilename, false, false, 0ms);
1750 
1751  QByteArray data;
1752  bool ret = rf->SaveAs(data);
1753 
1754  delete rf;
1755 
1756  if (ret)
1757  art.loadFromData(data);
1758  }
1759  else
1760  if (!imageFilename.isEmpty())
1761  art.load(imageFilename);
1762 
1763  if (art.isNull())
1764  {
1765  m_cursize = m_size;
1766  m_image = QImage();
1767  }
1768  else
1769  {
1770  m_image = art.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1771  }
1772  }
1773 
1774  if (m_image.isNull())
1775  {
1776  drawWarning(p, back, m_size, tr("?"), 100);
1777  return true;
1778  }
1779 
1780  // Paint the image
1781  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1782  p->drawImage((m_size.width() - m_image.width()) / 2,
1783  (m_size.height() - m_image.height()) / 2,
1784  m_image);
1785 
1786  // Store our new size
1787  m_cursize = m_size;
1788 
1789  return true;
1790 }
1791 
1792 static class AlbumArtFactory : public VisFactory
1793 {
1794  public:
1795  const QString &name(void) const override // VisFactory
1796  {
1797  static QString s_name = QCoreApplication::translate("Visualizers",
1798  "AlbumArt");
1799  return s_name;
1800  }
1801 
1802  uint plugins(QStringList *list) const override // VisFactory
1803  {
1804  *list << name();
1805  return 1;
1806  }
1807 
1808  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1809  {
1810  return new AlbumArt();
1811  }
1813 
1815  : VisualBase(true)
1816 {
1817  m_fps = 1;
1818 }
1819 
1820 void Blank::resize(const QSize &newsize)
1821 {
1822  m_size = newsize;
1823 }
1824 
1825 
1827 {
1828  return false;
1829 }
1830 
1831 bool Blank::draw(QPainter *p, const QColor &back)
1832 {
1833  // Took me hours to work out this algorithm
1834  p->fillRect(0, 0, m_size.width(), m_size.height(), back);
1835  return true;
1836 }
1837 
1838 static class BlankFactory : public VisFactory
1839 {
1840  public:
1841  const QString &name(void) const override // VisFactory
1842  {
1843  static QString s_name = QCoreApplication::translate("Visualizers",
1844  "Blank");
1845  return s_name;
1846  }
1847 
1848  uint plugins(QStringList *list) const override // VisFactory
1849  {
1850  *list << name();
1851  return 1;
1852  }
1853 
1854  VisualBase *create(MainVisual */*parent*/, const QString &/*pluginName*/) const override // VisFactory
1855  {
1856  return new Blank();
1857  }
1858 }BlankFactory;
Squares::Squares
Squares()
Definition: visualize.cpp:1093
Piano::piano_key_data::samples_processed
int samples_processed
Definition: visualize.h:278
Blank::Blank
Blank()
Definition: visualize.cpp:1814
gPlayer
MusicPlayer * gPlayer
Definition: musicplayer.cpp:37
StereoScopeFactory::name
const QString & name(void) const override
Definition: visualize.cpp:788
AlbumArtImage::m_imageType
ImageType m_imageType
Definition: musicmetadata.h:51
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
Spectrum::~Spectrum
~Spectrum() override
Definition: visualize.cpp:875
SpectrumFactory::name
const QString & name(void) const override
Definition: visualize.cpp:1070
Piano::piano_key_data
Definition: visualize.h:271
WF_HEIGHT
#define WF_HEIGHT
Definition: visualize.h:145
Piano::m_magnitude
std::vector< double > m_magnitude
Definition: visualize.h:318
Piano::piano_key_data::samples_process_before_display_update
int samples_process_before_display_update
Definition: visualize.h:279
PianoFactory
Definition: visualize.cpp:1599
AlbumArtFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1808
StereoScopeFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:795
AlbumArt::findFrontCover
void findFrontCover(void)
Definition: visualize.cpp:1628
PianoFactory::name
const QString & name(void) const override
Definition: visualize.cpp:1602
VisFactory::g_pVisFactories
static VisFactory * g_pVisFactories
Definition: visualize.h:102
Piano
Definition: visualize.h:254
AlbumArt::resize
void resize(const QSize &size) override
Definition: visualize.cpp:1676
WaveForm::m_right
short * m_right
Definition: visualize.h:163
Piano::kPianoNumKeys
static constexpr unsigned int kPianoNumKeys
Definition: visualize.h:261
Spectrum::m_scaleFactor
double m_scaleFactor
Definition: visualize.h:227
Piano::draw
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1490
back
static guint32 * back
Definition: goom_core.cpp:25
VisualNode
Definition: videovisual.h:25
Piano::processUndisplayed
bool processUndisplayed(VisualNode *node) override
Definition: visualize.cpp:1336
Piano::zero_analysis
void zero_analysis(void)
Definition: visualize.cpp:1230
Piano::getDesiredSamples
unsigned long getDesiredSamples(void) override
Definition: visualize.cpp:1326
WaveForm::m_sqrl
unsigned long m_sqrl
Definition: visualize.h:173
Spectrum::m_dftR
FFTComplex * m_dftR
Definition: visualize.h:232
Spectrum::process
bool process(VisualNode *node) override
Definition: visualize.cpp:920
MusicMetadata::getAlbumArtImages
AlbumArtImages * getAlbumArtImages(void)
Definition: musicmetadata.cpp:1358
StereoScope
Definition: visualize.h:110
AlbumArt::m_image
QImage m_image
Definition: visualize.h:342
LogScale::m_indices
int * m_indices
Definition: videovisualdefs.h:70
WaveForm::saveload
void saveload(MusicMetadata *meta)
Definition: visualize.cpp:547
LogScale::operator[]
int operator[](int index) const
Definition: videovisualdefs.h:63
WaveForm::draw
bool draw(QPainter *p, const QColor &back) override
Definition: visualize.cpp:731
WaveForm::handleKeyPress
void handleKeyPress(const QString &action) override
Definition: visualize.cpp:770
LogScale::m_r
int m_r
Definition: videovisualdefs.h:72
WaveForm::m_minr
short int m_minr
Definition: visualize.h:174
MonoScopeFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:821
WF_AUDIO_SIZE
#define WF_AUDIO_SIZE
Definition: visualize.h:143
mythdbcon.h
MythMainWindow::RestoreScreensaver
static void RestoreScreensaver()
Definition: mythmainwindow.cpp:574
AlbumArtFactory
Definition: visualize.cpp:1792
SAMPLES_DEFAULT_SIZE
static constexpr uint16_t SAMPLES_DEFAULT_SIZE
Definition: visualize.h:36
AlbumArt
Definition: visualize.h:321
MusicMetadata::ID
IdType ID() const
Definition: musicmetadata.h:218
RemoteFile
Definition: remotefile.h:17
StereoScope::m_rubberband
const bool m_rubberband
Definition: visualize.h:127
Spectrum::m_magnitudes
QVector< double > m_magnitudes
Definition: visualize.h:221
VisualBase
Definition: visualize.h:62
Blank::draw
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1831
BlankFactory
BlankFactory BlankFactory
StereoScope::resize
void resize(const QSize &size) override
Definition: visualize.cpp:169
StereoScope::m_falloff
const double m_falloff
Definition: visualize.h:128
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Spectrum::Spectrum
Spectrum()
Definition: visualize.cpp:863
Blank::m_size
QSize m_size
Definition: visualize.h:362
MusicMetadata
Definition: musicmetadata.h:80
SquaresFactory::name
const QString & name(void) const override
Definition: visualize.cpp:1156
MusicMetadata::Length
std::chrono::milliseconds Length() const
Definition: musicmetadata.h:205
StereoScope::process
bool process(VisualNode *node) override
Definition: visualize.cpp:179
LogScale::LogScale
LogScale(int maxscale=0, int maxrange=0)
Definition: videovisualdefs.h:9
mythdirs.h
StereoScope::m_size
QSize m_size
Definition: visualize.h:126
WaveForm::m_offset
unsigned long m_offset
Definition: visualize.h:162
WaveForm::m_showtext
bool m_showtext
Definition: visualize.h:165
MonoScopeFactory
MonoScopeFactory MonoScopeFactory
Piano::m_offsetProcessed
std::chrono::milliseconds m_offsetProcessed
Definition: visualize.h:313
MusicPlayer::getPlayMode
PlayMode getPlayMode(void)
Definition: musicplayer.h:77
WaveForm::m_font
QFont m_font
Definition: visualize.h:164
MonoScope
Definition: visualize.h:131
VisualNode::m_left
short * m_left
Definition: videovisual.h:37
VisualBase::VisualBase
VisualBase(bool screensaverenable=false)
Definition: visualize.cpp:46
AlbumArt::m_currImageType
ImageType m_currImageType
Definition: visualize.h:341
true
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:95
AlbumArtImages::getImage
AlbumArtImage * getImage(ImageType type)
Definition: musicmetadata.cpp:2100
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
WaveForm::m_position
unsigned int m_position
Definition: visualize.h:170
Spectrum::draw
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1032
WaveForm::m_duration
unsigned long m_duration
Definition: visualize.h:168
MusicPlayer::getCurrentMetadata
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
Definition: musicplayer.cpp:1160
MythMainWindow::DisableScreensaver
static void DisableScreensaver()
Definition: mythmainwindow.cpp:580
Piano::kPianoKeypressTooLight
static constexpr double kPianoKeypressTooLight
Definition: visualize.h:269
VisualNode::m_offset
std::chrono::milliseconds m_offset
Definition: videovisual.h:40
Spectrum::m_scale
LogScale m_scale
Definition: visualize.h:223
MonoScope::process
bool process(VisualNode *node) override
Definition: visualize.cpp:396
Spectrum::m_analyzerBarWidth
int m_analyzerBarWidth
Definition: visualize.h:229
Piano::m_whiteStartColor
QColor m_whiteStartColor
Definition: visualize.h:305
SpectrumFactory
Definition: visualize.cpp:1067
AlbumArt::draw
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1740
AlbumArtImages::getImageCount
uint getImageCount()
Definition: musicmetadata.h:530
Piano::m_blackTargetColor
QColor m_blackTargetColor
Definition: visualize.h:308
Squares
Definition: visualize.h:236
PianoFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1609
StereoScope::draw
bool draw(QPainter *p, const QColor &back) override
Definition: visualize.cpp:297
Piano::kPianoAudioSize
static constexpr unsigned long kPianoAudioSize
Definition: visualize.h:260
Squares::drawRect
void drawRect(QPainter *p, QRect *rect, int i, int c, int w, int h)
Definition: visualize.cpp:1105
WaveFormFactory
Definition: visualize.cpp:836
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
WF_WIDTH
#define WF_WIDTH
Definition: visualize.h:144
Piano::kPianoSpectrumSmoothing
static constexpr double kPianoSpectrumSmoothing
Definition: visualize.h:267
StereoScopeFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:801
remotefile.h
hardwareprofile.config.p
p
Definition: config.py:33
VisualNode::m_right
short * m_right
Definition: videovisual.h:38
hardwareprofile.i18n.t
t
Definition: i18n.py:36
LogScale::range
int range() const
Definition: videovisualdefs.h:20
MusicMetadata::getAlbumArtFile
QString getAlbumArtFile(void)
Definition: musicmetadata.cpp:1255
AlbumArt::cycleImage
bool cycleImage(void)
Definition: visualize.cpp:1647
Spectrum::m_startColor
QColor m_startColor
Definition: visualize.h:218
VisFactory
Definition: visualize.h:91
AlbumArt::m_cursize
QSize m_cursize
Definition: visualize.h:340
LogScale::setMax
void setMax(int maxscale, int maxrange)
Definition: videovisualdefs.h:22
AlbumArtImages::getImageAt
AlbumArtImage * getImageAt(uint index)
Definition: musicmetadata.cpp:2132
Piano::piano_key_data::q2
goertzel_data q2
Definition: visualize.h:272
AlbumArt::needsUpdate
bool needsUpdate(void)
Definition: visualize.cpp:1720
AlbumArt::process
bool process(VisualNode *node=nullptr) override
Definition: visualize.cpp:1681
hardwareprofile.smolt.long
long
Definition: smolt.py:76
StereoScope::m_targetColor
QColor m_targetColor
Definition: visualize.h:124
Squares::m_fakeHeight
int m_fakeHeight
Definition: visualize.h:250
VisualBase::~VisualBase
virtual ~VisualBase(void)
Definition: visualize.cpp:53
goertzel_data
#define goertzel_data
Definition: visualize.h:264
WaveFormFactory::name
const QString & name(void) const override
Definition: visualize.cpp:839
Piano::resize
void resize(const QSize &size) override
Definition: visualize.cpp:1246
inlines.h
sizetliteral.h
visualize.h
StereoScope::m_magnitudes
std::vector< double > m_magnitudes
Definition: visualize.h:125
WaveForm
Definition: visualize.h:147
uint
unsigned int uint
Definition: compat.h:79
WaveForm::m_lastx
unsigned int m_lastx
Definition: visualize.h:169
FFTW_N
static constexpr int FFTW_N
Definition: visualize.cpp:40
ALBUMARTCYCLETIME
static constexpr qint64 ALBUMARTCYCLETIME
this is the time an image is shown in the albumart visualizer
Definition: visualize.cpp:1718
WaveForm::m_currentMetadata
MusicMetadata * m_currentMetadata
Definition: visualize.h:167
SquaresFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1169
AlbumArt::m_currentMetadata
MusicMetadata * m_currentMetadata
Definition: visualize.h:344
WaveForm::getDesiredSamples
unsigned long getDesiredSamples(void) override
Definition: visualize.cpp:600
AlbumArtFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1802
LogScale::m_s
int m_s
Definition: videovisualdefs.h:71
SpectrumFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1083
LogScale::~LogScale
~LogScale()
Definition: videovisualdefs.h:14
StereoScope::m_startColor
QColor m_startColor
Definition: visualize.h:123
WaveFormFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:852
Piano::piano_key_data::magnitude
goertzel_data magnitude
Definition: visualize.h:272
SquaresFactory
Definition: visualize.cpp:1153
sq
T sq(T a)
Definition: visualize.cpp:918
SquaresFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1163
WaveFormFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:846
Squares::m_actualSize
QSize m_actualSize
Definition: visualize.h:249
AlbumArt::m_lastCycle
QDateTime m_lastCycle
Definition: visualize.h:345
WaveForm::m_minl
short int m_minl
Definition: visualize.h:171
StereoScopeFactory
StereoScopeFactory StereoScopeFactory
Piano::kPianoRmsNegligible
static constexpr double kPianoRmsNegligible
Definition: visualize.h:266
mythuihelper.h
MainVisual
Definition: mainvisual.h:34
WaveForm::m_maxl
short int m_maxl
Definition: visualize.h:172
Piano::m_audioData
piano_audio * m_audioData
Definition: visualize.h:316
IT_UNKNOWN
@ IT_UNKNOWN
Definition: musicmetadata.h:30
AlbumArt::handleKeyPress
void handleKeyPress(const QString &action) override
Definition: visualize.cpp:1686
VisualNode::m_length
long m_length
Definition: videovisual.h:39
StereoScope::StereoScope
StereoScope()
Definition: visualize.cpp:164
WaveForm::m_sqrr
unsigned long m_sqrr
Definition: visualize.h:176
VisualBase::m_fps
int m_fps
Definition: visualize.h:87
Spectrum::m_falloff
double m_falloff
Definition: visualize.h:228
Piano::piano_key_data::coeff
goertzel_data coeff
Definition: visualize.h:272
MonoScope::draw
bool draw(QPainter *p, const QColor &back) override
Definition: visualize.cpp:487
Piano::m_pianoData
piano_key_data * m_pianoData
Definition: visualize.h:315
Spectrum::resize
void resize(const QSize &size) override
Definition: visualize.cpp:882
AlbumArtImages
Definition: musicmetadata.h:520
MonoScopeFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:827
Squares::draw
bool draw(QPainter *p, const QColor &back=Qt::black) override
Definition: visualize.cpp:1139
WaveForm::process_all_types
bool process_all_types(VisualNode *node, bool displayed)
Definition: visualize.cpp:634
PianoFactory
PianoFactory PianoFactory
Spectrum::m_fftContextForward
FFTContext * m_fftContextForward
Definition: visualize.h:233
Piano::piano_key_data::is_black_note
bool is_black_note
Definition: visualize.h:281
WaveForm::processUndisplayed
bool processUndisplayed(VisualNode *node) override
Definition: visualize.cpp:605
SquaresFactory
SquaresFactory SquaresFactory
BlankFactory::name
const QString & name(void) const override
Definition: visualize.cpp:1841
Piano::m_size
QSize m_size
Definition: visualize.h:311
AlbumArt::AlbumArt
AlbumArt(void)
Definition: visualize.cpp:1621
VisualBase::m_xscreensaverenable
bool m_xscreensaverenable
Definition: visualize.h:88
Squares::resize
void resize(const QSize &newsize) override
Definition: visualize.cpp:1098
AlbumArtFactory::name
const QString & name(void) const override
Definition: visualize.cpp:1795
WaveForm::m_maxr
short int m_maxr
Definition: visualize.h:175
WaveFormFactory
WaveFormFactory WaveFormFactory
IT_LAST
@ IT_LAST
Definition: musicmetadata.h:36
Piano::kPianoMinVol
static constexpr goertzel_data kPianoMinVol
Definition: visualize.h:268
Spectrum::m_dftL
FFTComplex * m_dftL
Definition: visualize.h:231
Blank::resize
void resize(const QSize &size) override
Definition: visualize.cpp:1820
common.utilities.log
def log(debug, txt)
Definition: utilities.py:5
MonoScopeFactory::name
const QString & name(void) const override
Definition: visualize.cpp:814
Piano::m_whiteTargetColor
QColor m_whiteTargetColor
Definition: visualize.h:306
mythcontext.h
AlbumArtFactory
AlbumArtFactory AlbumArtFactory
mainvisual.h
Piano::process
bool process(VisualNode *node) override
Definition: visualize.cpp:1342
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
build_compdb.action
action
Definition: build_compdb.py:9
BlankFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1854
MusicPlayer::PLAYMODE_RADIO
@ PLAYMODE_RADIO
Definition: musicplayer.h:73
Blank
Definition: visualize.h:348
Spectrum
Definition: visualize.h:199
IT_FRONTCOVER
@ IT_FRONTCOVER
Definition: musicmetadata.h:31
Blank::process
bool process(VisualNode *node=nullptr) override
Definition: visualize.cpp:1826
WaveForm::process
bool process(VisualNode *node) override
Definition: visualize.cpp:618
SpectrumFactory
SpectrumFactory SpectrumFactory
Piano::Piano
Piano()
Definition: visualize.cpp:1176
StereoScopeFactory
Definition: visualize.cpp:785
Spectrum::m_rects
QVector< QRect > m_rects
Definition: visualize.h:220
Piano::clamp
static double clamp(double cur, double max, double min)
Definition: visualize.cpp:1481
MonoScopeFactory
Definition: visualize.cpp:811
Piano::~Piano
~Piano() override
Definition: visualize.cpp:1222
Piano::piano_key_data::q1
goertzel_data q1
Definition: visualize.h:272
Piano::m_blackStartColor
QColor m_blackStartColor
Definition: visualize.h:307
build_compdb.filename
filename
Definition: build_compdb.py:21
MythUIScreenBounds::GetScalingFactors
void GetScalingFactors(float &Horizontal, float &Vertical) const
Definition: mythuiscreenbounds.cpp:235
mythmainwindow.h
Squares::m_numberOfSquares
int m_numberOfSquares
Definition: visualize.h:251
Spectrum::clamp
static double clamp(double cur, double max, double min)
Definition: visualize.cpp:1023
AlbumArt::m_size
QSize m_size
Definition: visualize.h:339
WaveForm::~WaveForm
~WaveForm() override
Definition: visualize.cpp:541
PianoFactory::create
VisualBase * create(MainVisual *, const QString &) const override
Definition: visualize.cpp:1615
ImageType
ImageType
Definition: musicmetadata.h:28
Piano::m_rects
std::vector< QRect > m_rects
Definition: visualize.h:310
musicmetadata.h
Piano::process_all_types
bool process_all_types(VisualNode *node, bool this_will_be_displayed)
Definition: visualize.cpp:1352
Piano::piano_key_data::max_magnitude_seen
goertzel_data max_magnitude_seen
Definition: visualize.h:273
VisualBase::drawWarning
static void drawWarning(QPainter *p, const QColor &back, QSize size, const QString &warning, int fontsize=28)
Definition: visualize.cpp:65
decoder.h
M_PI
static constexpr double M_PI
Definition: goom_tools.h:9
WaveForm::m_image
QImage m_image
Definition: visualize.h:166
Spectrum::m_targetColor
QColor m_targetColor
Definition: visualize.h:219
Spectrum::m_size
QSize m_size
Definition: visualize.h:222
musicplayer.h
BlankFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1848
piano_audio
#define piano_audio
Definition: visualize.h:263
SpectrumFactory::plugins
uint plugins(QStringList *list) const override
Definition: visualize.cpp:1077
BlankFactory
Definition: visualize.cpp:1838