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