MythTV  master
videovisualspectrum.cpp
Go to the documentation of this file.
1 // QT
2 #include <QPen>
3 
4 // MythTV
5 #include "videovisualspectrum.h"
6 
7 // FFmpeg
8 extern "C" {
9 #include "libavutil/mem.h"
10 #include "libavutil/tx.h"
11 }
12 static constexpr int k_FFT_sample_length { 512 };
13 
14 // Std
15 #include <algorithm>
16 
18 
20  : VideoVisual(Audio, Render),
21  m_dftL(static_cast<AVComplexFloat*>(av_malloc(sizeof(AVComplexFloat) * k_FFT_sample_length))),
22  m_dftR(static_cast<AVComplexFloat*>(av_malloc(sizeof(AVComplexFloat) * k_FFT_sample_length)))
23 {
24  // If av_tx_init() fails (returns < 0), the contexts will be nullptr and will crash later,
25  // but av_malloc() is not checked to succeed either.
26  av_tx_init(&m_fftContext , &m_fft , AV_TX_FLOAT_FFT, 0, k_FFT_sample_length, &kScale, AV_TX_INPLACE);
27 }
28 
30 {
31  av_freep(reinterpret_cast<void*>(&m_dftL));
32  av_freep(reinterpret_cast<void*>(&m_dftR));
33  av_tx_uninit(&m_fftContext);
34 }
35 
36 template<typename T> T sq(T a) { return a*a; };
37 
38 void VideoVisualSpectrum::Draw(const QRect Area, MythPainter* Painter, QPaintDevice* Device)
39 {
40  if (m_disabled)
41  return;
42 
43  uint i = 0;
44  {
45  QMutexLocker locker(mutex());
46  VisualNode* node = GetNode();
47  if (Area.isEmpty() || !Painter)
48  return;
49 
50  if (!Initialise(Area))
51  return;
52 
53  if (node)
54  {
55  i = static_cast<uint>(node->m_length);
56  for (auto k = 0; k < node->m_length; k++)
57  {
58  m_dftL[k] = (AVComplexFloat){ .re = static_cast<float>(node->m_left[k]), .im = 0 };
59  if (node->m_right)
60  m_dftR[k] = (AVComplexFloat){ .re = static_cast<float>(node->m_right[k]), .im = 0 };
61  }
62  }
63  }
64 
65  for (auto k = i; k < k_FFT_sample_length; k++)
66  {
67  m_dftL[k] = (AVComplexFloat){ .re = 0, .im = 0 };
68  m_dftR[k] = (AVComplexFloat){ .re = 0, .im = 0 };
69  }
70  m_fft(m_fftContext, m_dftL, m_dftL, sizeof(AVComplexFloat));
71  m_fft(m_fftContext, m_dftR, m_dftR, sizeof(AVComplexFloat));
72 
73  double falloff = std::clamp(((static_cast<double>(SetLastUpdate().count())) / 40.0) * m_falloff, 0.0, 2048.0);
74  for (int l = 0, r = m_scale.range(); l < m_scale.range(); l++, r++)
75  {
76  int index = m_scale[l];
77 
78  // The 1D output is Hermitian symmetric (Yk = Yn-k) so Yn = Y0 etc.
79  // The dft_r2c_1d plan doesn't output these redundant values
80  // and furthermore they're not allocated in the ctor
81  double tmp = 2 * sq(m_dftL[index].re);
82  double magL = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
83 
84  tmp = 2 * sq(m_dftR[index].re);
85  double magR = (tmp > 1.) ? (log(tmp) - 22.0) * m_scaleFactor : 0.;
86  if (magL > m_range)
87  magL = 1.0;
88 
89  if (magL < m_magnitudes[l])
90  {
91  tmp = m_magnitudes[l] - falloff;
92  tmp = std::max(tmp, magL);
93  magL = tmp;
94  }
95 
96  magL = std::max(magL, 1.0);
97 
98  if (magR > m_range)
99  magR = 1.0;
100 
101  if (magR < m_magnitudes[r])
102  {
103  tmp = m_magnitudes[r] - falloff;
104  tmp = std::max(tmp, magR);
105  magR = tmp;
106  }
107 
108  magR = std::max(magR, 1.0);
109 
110  m_magnitudes[l] = magL;
111  m_magnitudes[r] = magR;
112  }
113 
114  DrawPriv(Painter, Device);
115 }
116 
118 {
119  std::fill(m_magnitudes.begin(), m_magnitudes.end(), 0.0);
121 }
122 
123 void VideoVisualSpectrum::DrawPriv(MythPainter* Painter, QPaintDevice* Device)
124 {
125  static const QBrush kBrush(QColor(0, 0, 200, 180));
126  static const QPen kPen(QColor(255, 255, 255, 255));
127  double range = m_area.top() + (m_area.height() / 2.0);
128  int count = m_scale.range();
129  Painter->Begin(Device);
130  for (int i = 0; i < count; i++)
131  {
132  m_rects[i].setTop(static_cast<int>(range - static_cast<int>(m_magnitudes[i])));
133  m_rects[i].setBottom(static_cast<int>(range + static_cast<int>(m_magnitudes[i + count])));
134  if (m_rects[i].height() > 4)
135  Painter->DrawRect(m_rects[i], kBrush, kPen, 255);
136  }
137  Painter->End();
138 }
139 
140 bool VideoVisualSpectrum::Initialise(const QRect Area)
141 {
142  if (Area == m_area)
143  return true;
144 
145  m_area = Area;
146  m_barWidth = m_area.width() / m_numSamples;
147  m_barWidth = std::max(m_barWidth, 6);
148  m_scale.setMax(192, m_area.width() / m_barWidth);
149 
150  m_magnitudes.resize(m_scale.range() * 2);
151  std::fill(m_magnitudes.begin(), m_magnitudes.end(), 0.0);
152  InitialisePriv();
153  return true;
154 }
155 
157 {
158  m_range = m_area.height() / 2.0;
159  m_rects.resize(m_scale.range());
160  int y = m_area.top() + static_cast<int>(m_range);
161  for (int i = 0, x = m_area.left(); i < m_rects.size(); i++, x+= m_barWidth)
162  m_rects[i].setRect(x, y, m_barWidth - 1, 1);
163 
164  m_scaleFactor = (static_cast<double>(m_area.height()) / 2.0) / log(static_cast<double>(k_FFT_sample_length));
165  m_falloff = static_cast<double>(m_area.height()) / 150.0;
166 
167  LOG(VB_GENERAL, LOG_INFO, DESC + QString("Initialised Spectrum with %1 bars").arg(m_scale.range()));
168  return true;
169 }
170 
172 {
173  public:
174  const QString &name() const override;
175  VideoVisual *Create(AudioPlayer* Audio, MythRender* Render) const override;
176  bool SupportedRenderer(RenderType /*Type*/) override { return true; }
178 
179 const QString& VideoVisualSpectrumFactory::name() const
180 {
181  static QString s_name(SPECTRUM_NAME);
182  return s_name;
183 }
184 
186 {
187  return new VideoVisualSpectrum(Audio, Render);
188 }
VideoVisualSpectrum::m_scale
LogScale m_scale
Definition: videovisualspectrum.h:31
k_FFT_sample_length
static constexpr int k_FFT_sample_length
Definition: videovisualspectrum.cpp:12
VideoVisualSpectrum::InitialisePriv
virtual bool InitialisePriv()
Definition: videovisualspectrum.cpp:156
VideoVisualSpectrum::m_dftL
AVComplexFloat * m_dftL
Definition: videovisualspectrum.h:35
AudioPlayer
Definition: audioplayer.h:22
VisualNode
Definition: videovisual.h:24
VideoVisualSpectrum::m_rects
QVector< QRect > m_rects
Definition: videovisualspectrum.h:43
VideoVisual::prepare
void prepare() override
Definition: videovisual.cpp:79
VideoVisual::m_disabled
bool m_disabled
Definition: videovisual.h:70
VideoVisualSpectrumFactory::name
const QString & name() const override
Definition: videovisualspectrum.cpp:179
VideoVisualSpectrum::kScale
static constexpr float kScale
Definition: videovisualspectrum.h:37
VideoVisualSpectrum::Initialise
virtual bool Initialise(QRect Area)
Definition: videovisualspectrum.cpp:140
VideoVisualSpectrum::m_magnitudes
QVector< double > m_magnitudes
Definition: videovisualspectrum.h:29
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythPainter::DrawRect
virtual void DrawRect(QRect area, const QBrush &fillBrush, const QPen &linePen, int alpha)
Definition: mythpainter.cpp:156
Device
A device containing images (ie. USB stick, CD, storage group etc)
Definition: imagemanager.cpp:37
sq
T sq(T a)
Definition: videovisualspectrum.cpp:36
VisualNode::m_left
short * m_left
Definition: videovisual.h:36
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
VideoVisualSpectrum::m_barWidth
int m_barWidth
Definition: videovisualspectrum.h:44
VideoVisualSpectrum
Definition: videovisualspectrum.h:13
mythlogging.h
DESC
#define DESC
Definition: videovisual.h:19
VideoVisualSpectrum::m_range
double m_range
Definition: videovisualspectrum.h:30
VideoVisualSpectrum::m_dftR
AVComplexFloat * m_dftR
Definition: videovisualspectrum.h:36
VisualNode::m_right
short * m_right
Definition: videovisual.h:37
LogScale::range
int range() const
Definition: videovisualdefs.h:16
VideoVisualFactory
Definition: videovisual.h:77
MythTV::Visual::mutex
QMutex * mutex()
Definition: visual.h:26
LogScale::setMax
void setMax(int maxscale, int maxrange)
Definition: videovisualdefs.h:18
VideoVisualSpectrumFactory::SupportedRenderer
bool SupportedRenderer(RenderType) override
Definition: videovisualspectrum.cpp:176
VideoVisualSpectrum::m_falloff
double m_falloff
Definition: videovisualspectrum.h:33
RenderType
RenderType
Definition: mythrender_base.h:15
VideoVisualSpectrum::VideoVisualSpectrum
VideoVisualSpectrum(AudioPlayer *Audio, MythRender *Render)
Definition: videovisualspectrum.cpp:19
VideoVisualSpectrum::m_fftContext
AVTXContext * m_fftContext
Definition: videovisualspectrum.h:38
VideoVisualSpectrumFactory
Definition: videovisualspectrum.cpp:171
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
VideoVisualSpectrum::m_fft
av_tx_fn m_fft
Definition: videovisualspectrum.h:39
MythPainter::End
virtual void End()
Definition: mythpainter.h:55
VideoVisualSpectrum::~VideoVisualSpectrum
~VideoVisualSpectrum() override
Definition: videovisualspectrum.cpp:29
VideoVisualSpectrumFactory::Create
VideoVisual * Create(AudioPlayer *Audio, MythRender *Render) const override
Definition: videovisualspectrum.cpp:185
VisualNode::m_length
long m_length
Definition: videovisual.h:38
VideoVisualSpectrum::m_numSamples
int m_numSamples
Definition: videovisualspectrum.h:28
SPECTRUM_NAME
#define SPECTRUM_NAME
Definition: videovisualspectrum.h:11
MythRender
Definition: mythrender_base.h:23
MythPainter
Definition: mythpainter.h:34
VideoVisualSpectrumFactory
VideoVisualSpectrumFactory VideoVisualSpectrumFactory
videovisualspectrum.h
VideoVisualSpectrum::prepare
void prepare() override
Definition: videovisualspectrum.cpp:117
VideoVisual
Definition: videovisual.h:42
VideoVisualSpectrum::Draw
void Draw(QRect Area, MythPainter *Painter, QPaintDevice *Device) override
Definition: videovisualspectrum.cpp:38
VideoVisualSpectrum::DrawPriv
virtual void DrawPriv(MythPainter *Painter, QPaintDevice *Device)
Definition: videovisualspectrum.cpp:123
VideoVisualSpectrum::m_scaleFactor
double m_scaleFactor
Definition: videovisualspectrum.h:32
VideoVisual::m_area
QRect m_area
Definition: videovisual.h:71
VideoVisual::GetNode
VisualNode * GetNode(void)
Definition: videovisual.cpp:85
xbmc.log
None log(str msg, int level=LOGDEBUG)
Definition: xbmc.py:9
MythPainter::Begin
virtual void Begin(QPaintDevice *)
Definition: mythpainter.h:54
VideoVisual::SetLastUpdate
std::chrono::milliseconds SetLastUpdate(void)
Definition: videovisual.cpp:60
uint
unsigned int uint
Definition: freesurround.h:24