Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

libmythtv: Add some visualisation files.

These are not yet hooked up.
  • Loading branch information...
commit cc80dacdb55d162fe7c45bdb57e06fcc50e53b9f 1 parent 68578e0
Mark Kendall authored
View
147 mythtv/libs/libmythtv/visualisations/videovisual.cpp
@@ -0,0 +1,147 @@
+#include "mythrender_base.h"
+#include "mythplayer.h"
+#include "videovisual.h"
+
+#ifdef FFTW3_SUPPORT
+#include "videovisualspectrum.h"
+#include "videovisualcircles.h"
+#endif
+
+#ifdef USING_OPENGL
+#include "mythrender_opengl2.h"
+#endif
+
+bool VideoVisual::CanVisualise(AudioPlayer *audio, MythRender *render)
+{
+#ifdef FFTW3_SUPPORT
+ if (render && audio->GetNumChannels() == 2)
+ return true;
+#endif
+ return false;
+}
+
+VideoVisual* VideoVisual::Create(AudioPlayer *audio, MythRender *render)
+{
+#ifdef FFTW3_SUPPORT
+ if (render)
+ {
+#ifdef USING_OPENGL
+ if (dynamic_cast<MythRenderOpenGL2*>(render))
+ return new VideoVisualCircles(audio, render);
+#endif
+ return new VideoVisualSpectrum(audio, render);
+ }
+#endif
+ return NULL;
+}
+
+VideoVisual::VideoVisual(AudioPlayer *audio, MythRender *render)
+ : m_audio(audio), m_disabled(false), m_area(QRect()), m_render(render)
+{
+ m_lastUpdate = QDateTime::currentDateTime();
+ mutex()->lock();
+ if (m_audio)
+ m_audio->addVisual(this);
+ mutex()->unlock();
+}
+
+VideoVisual::~VideoVisual()
+{
+ mutex()->lock();
+ if (m_audio)
+ m_audio->removeVisual(this);
+ DeleteNodes();
+ mutex()->unlock();
+}
+
+int64_t VideoVisual::SetLastUpdate(void)
+{
+ QDateTime now = QDateTime::currentDateTime();
+ int64_t result = m_lastUpdate.msecsTo(now);
+ m_lastUpdate = now;
+ return result;
+}
+
+// caller holds lock
+void VideoVisual::DeleteNodes(void)
+{
+ while (!m_nodes.empty())
+ {
+ delete m_nodes.back();
+ m_nodes.pop_back();
+ }
+}
+
+// caller holds lock
+void VideoVisual::prepare()
+{
+ DeleteNodes();
+}
+
+// caller holds lock
+VisualNode* VideoVisual::GetNode(void)
+{
+ int64_t timestamp = m_audio->GetAudioTime();
+ while (m_nodes.size() > 1)
+ {
+ if (m_nodes.front()->offset > timestamp)
+ break;
+ delete m_nodes.front();
+ m_nodes.pop_front();
+ }
+
+ if (m_nodes.isEmpty())
+ return NULL;
+
+ return m_nodes.first();
+}
+
+// caller holds lock
+void VideoVisual::add(uchar *b, unsigned long b_len, unsigned long w, int c, int p)
+{
+ if (!m_disabled && m_nodes.size() > 500)
+ {
+ VERBOSE(VB_GENERAL, DESC +
+ QString("Over 500 nodes buffered - disabling visualiser."));
+ DeleteNodes();
+ m_disabled = true;
+ }
+
+ if (m_disabled)
+ return;
+
+ long len = b_len, cnt;
+ short *l = 0, *r = 0;
+
+ len /= c;
+ len /= (p / 8);
+
+ if (len > 512)
+ len = 512;
+
+ cnt = len;
+
+ if (c == 2)
+ {
+ l = new short[len];
+ r = new short[len];
+
+ if (p == 8)
+ stereo16_from_stereopcm8(l, r, b, cnt);
+ else if (p == 16)
+ stereo16_from_stereopcm16(l, r, (short *) b, cnt);
+ }
+ else if (c == 1)
+ {
+ l = new short[len];
+
+ if (p == 8)
+ mono16_from_monopcm8(l, b, cnt);
+ else if (p == 16)
+ mono16_from_monopcm16(l, (short *) b, cnt);
+ }
+ else
+ len = 0;
+
+ m_nodes.append(new VisualNode(l, r, len, w));
+}
View
62 mythtv/libs/libmythtv/visualisations/videovisual.h
@@ -0,0 +1,62 @@
+#ifndef VIDEOVISUAL_H
+#define VIDEOVISUAL_H
+
+#include <QRect>
+#include <QList>
+#include "stdint.h"
+
+#include "mythverbose.h"
+#include "visual.h"
+#include "mythpainter.h"
+#include "videovisualdefs.h"
+
+#define DESC QString("Visualiser: ")
+
+class MythRender;
+class AudioPlayer;
+
+class VisualNode
+{
+ public:
+ VisualNode(short *l, short *r, unsigned long n, unsigned long o)
+ : left(l), right(r), length(n), offset(o) { }
+
+ ~VisualNode()
+ {
+ delete [] left;
+ delete [] right;
+ }
+
+ short *left, *right;
+ long length, offset;
+};
+
+class VideoVisual : public MythTV::Visual
+{
+ public:
+ static bool CanVisualise(AudioPlayer *audio, MythRender *render);
+ static VideoVisual* Create(AudioPlayer *audio, MythRender *render);
+
+ VideoVisual(AudioPlayer *audio, MythRender *render);
+ ~VideoVisual();
+
+ virtual void Draw(const QRect &area, MythPainter *painter,
+ QPaintDevice* device) = 0;
+
+ virtual void add(uchar *b, unsigned long b_len, unsigned long w, int c, int p);
+ virtual void prepare();
+
+ protected:
+ VisualNode* GetNode(void);
+ void DeleteNodes(void);
+ int64_t SetLastUpdate(void);
+
+ AudioPlayer *m_audio;
+ bool m_disabled;
+ QRect m_area;
+ MythRender *m_render;
+ QList<VisualNode*> m_nodes;
+ QDateTime m_lastUpdate;
+};
+
+#endif // VIDEOVISUAL_H
View
47 mythtv/libs/libmythtv/visualisations/videovisualcircles.cpp
@@ -0,0 +1,47 @@
+#include <QPen>
+#include "videovisualcircles.h"
+
+VideoVisualCircles::VideoVisualCircles(AudioPlayer *audio, MythRender *render)
+ : VideoVisualSpectrum(audio, render)
+{
+ m_numSamples = 32;
+}
+
+void VideoVisualCircles::DrawPriv(MythPainter *painter, QPaintDevice* device)
+{
+ if (!painter)
+ return;
+
+ static const QBrush nobrush(Qt::NoBrush);
+ int red = 0, green = 200;
+ QPen pen(QColor(red, green, 0, 255));
+ int count = m_scale.range();
+ int incr = 200 / count;
+ int rad = m_range;
+ QRect circ(m_area.x() + m_area.width() / 2, m_area.y() + m_area.height() / 2,
+ rad, rad);
+ painter->Begin(device);
+ for (int i = 0; i < count; i++, rad += m_range, red += incr, green -= incr)
+ {
+ double mag = abs((m_magnitudes[i] + m_magnitudes[i + count]) / 2.0);
+ if (mag > 1.0)
+ {
+ pen.setWidth((int)mag);
+ painter->DrawRoundRect(circ, rad, nobrush, pen, 200);
+ }
+ circ.adjust(-m_range, -m_range, m_range, m_range);
+ pen.setColor(QColor(red, green, 0, 255));
+ }
+ painter->End();
+}
+
+bool VideoVisualCircles::InitialisePriv(void)
+{
+ m_range = (m_area.height() / 2) / (m_scale.range() -10);
+ m_scaleFactor = 10.0;
+ m_falloff = 1.0;
+
+ VERBOSE(VB_GENERAL, DESC + QString("Initialised Circles with %1 circles.")
+ .arg(m_scale.range()));
+ return true;
+}
View
16 mythtv/libs/libmythtv/visualisations/videovisualcircles.h
@@ -0,0 +1,16 @@
+#ifndef VIDEOVISUALCIRCLES_H
+#define VIDEOVISUALCIRCLES_H
+
+#include "videovisualspectrum.h"
+
+class VideoVisualCircles : public VideoVisualSpectrum
+{
+ public:
+ VideoVisualCircles(AudioPlayer *audio, MythRender *render);
+
+ protected:
+ virtual bool InitialisePriv(void);
+ virtual void DrawPriv(MythPainter *painter, QPaintDevice* device);
+};
+
+#endif // VIDEOVISUALCIRCLES_H
View
314 mythtv/libs/libmythtv/visualisations/videovisualdefs.h
@@ -0,0 +1,314 @@
+#ifndef VIDEOVISUAL_DEFS_H
+#define VIDEOVISUAL_DEFS_H
+
+#include <cmath>
+
+class LogScale
+{
+ public:
+ LogScale(int maxscale = 0, int maxrange = 0)
+ : indices(0), s(0), r(0)
+ {
+ setMax(maxscale, maxrange);
+ }
+
+ ~LogScale()
+ {
+ if (indices)
+ delete [] indices;
+ }
+
+ int scale() const { return s; }
+ int range() const { return r; }
+
+ void setMax(int maxscale, int maxrange)
+ {
+ if (maxscale == 0 || maxrange == 0)
+ return;
+
+ s = maxscale;
+ r = maxrange;
+
+ if (indices)
+ delete [] indices;
+
+ double alpha;
+ int i, scaled;
+ long double domain = (long double) maxscale;
+ long double range = (long double) maxrange;
+ long double x = 1.0;
+ long double dx = 1.0;
+ long double y = 0.0;
+ long double yy = 0.0;
+ long double t = 0.0;
+ long double e4 = 1.0E-8;
+
+ indices = new int[maxrange];
+ for (i = 0; i < maxrange; i++)
+ indices[i] = 0;
+
+ // initialize log scale
+ for (uint i = 0; i < 10000 && (std::abs(dx) > e4); i++)
+ {
+ t = std::log((domain + x) / x);
+ y = (x * t) - range;
+ yy = t - (domain / (x + domain));
+ dx = y / yy;
+ x -= dx;
+ }
+
+ alpha = x;
+ for (i = 1; i < (int) domain; i++)
+ {
+ scaled = (int) floor(0.5 + (alpha * log((double(i) + alpha) / alpha)));
+ if (scaled < 1)
+ scaled = 1;
+ if (indices[scaled - 1] < i)
+ indices[scaled - 1] = i;
+ }
+ }
+
+ int operator[](int index)
+ {
+ return indices[index];
+ }
+
+
+ private:
+ int *indices;
+ int s, r;
+};
+
+static inline void stereo16_from_stereopcm8(register short *l,
+ register short *r,
+ register uchar *c,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = c[0];
+ r[0] = c[1];
+ l[1] = c[2];
+ r[1] = c[3];
+ l[2] = c[4];
+ r[2] = c[5];
+ l[3] = c[6];
+ r[3] = c[7];
+ l += 4;
+ r += 4;
+ c += 8;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = c[0];
+ r[0] = c[1];
+ if (cnt > 1l)
+ {
+ l[1] = c[2];
+ r[1] = c[3];
+ if (cnt > 2l)
+ {
+ l[2] = c[4];
+ r[2] = c[5];
+ }
+ }
+ }
+}
+
+static inline void stereo16_from_stereopcm16(register short *l,
+ register short *r,
+ register short *s,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = s[0];
+ r[0] = s[1];
+ l[1] = s[2];
+ r[1] = s[3];
+ l[2] = s[4];
+ r[2] = s[5];
+ l[3] = s[6];
+ r[3] = s[7];
+ l += 4;
+ r += 4;
+ s += 8;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = s[0];
+ r[0] = s[1];
+ if (cnt > 1l)
+ {
+ l[1] = s[2];
+ r[1] = s[3];
+ if (cnt > 2l)
+ {
+ l[2] = s[4];
+ r[2] = s[5];
+ }
+ }
+ }
+}
+
+static inline void mono16_from_monopcm8(register short *l,
+ register uchar *c,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = c[0];
+ l[1] = c[1];
+ l[2] = c[2];
+ l[3] = c[3];
+ l += 4;
+ c += 4;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = c[0];
+ if (cnt > 1l)
+ {
+ l[1] = c[1];
+ if (cnt > 2l)
+ {
+ l[2] = c[2];
+ }
+ }
+ }
+}
+
+static inline void mono16_from_monopcm16(register short *l,
+ register short *s,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = s[0];
+ l[1] = s[1];
+ l[2] = s[2];
+ l[3] = s[3];
+ l += 4;
+ s += 4;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = s[0];
+ if (cnt > 1l)
+ {
+ l[1] = s[1];
+ if (cnt > 2l)
+ {
+ l[2] = s[2];
+ }
+ }
+ }
+}
+
+#ifdef FFTW3_SUPPORT
+
+#include <complex>
+extern "C" {
+#include <fftw3.h>
+#define myth_fftw_float double /* need to use different plan function to change */
+#define fftw_real myth_fftw_float
+#define myth_fftw_complex std::complex<myth_fftw_float>
+#if (myth_fftw_float == double)
+#define myth_fftw_complex_cast fftw_complex
+#elif (myth_fftw_float == float)
+#define myth_fftw_complex_cast fftwf_complex
+#endif
+}
+
+static inline void fast_short_set(register short *p,
+ short v,
+ long c)
+{
+ while (c >= 4l) {
+ p[0] = v;
+ p[1] = v;
+ p[2] = v;
+ p[3] = v;
+ p += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l) {
+ p[0] = v;
+ if (c > 1l) {
+ p[1] = v;
+ if (c > 2l) {
+ p[2] = v;
+ }
+ }
+ }
+}
+
+static inline void fast_real_set_from_short(register fftw_real *d,
+ register short *s,
+ long c)
+{
+ while (c >= 4l) {
+ d[0] = fftw_real(s[0]);
+ d[1] = fftw_real(s[1]);
+ d[2] = fftw_real(s[2]);
+ d[3] = fftw_real(s[3]);
+ d += 4;
+ s += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l) {
+ d[0] = fftw_real(s[0]);
+ if (c > 1l) {
+ d[1] = fftw_real(s[1]);
+ if (c > 2l) {
+ d[2] = fftw_real(s[2]);
+ }
+ }
+ }
+}
+
+static inline void fast_reals_set(register fftw_real *p1,
+ register fftw_real *p2,
+ fftw_real v,
+ long c)
+{
+ while (c >= 4l) {
+ p1[0] = v;
+ p1[1] = v;
+ p1[2] = v;
+ p1[3] = v;
+ p2[0] = v;
+ p2[1] = v;
+ p2[2] = v;
+ p2[3] = v;
+ p1 += 4;
+ p2 += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l) {
+ p1[0] = v;
+ p2[0] = v;
+ if (c > 1l) {
+ p1[1] = v;
+ p2[1] = v;
+ if (c > 2l) {
+ p1[2] = v;
+ p2[2] = v;
+ }
+ }
+ }
+}
+
+#endif // FFTW3_SUPPORT
+#endif // VIDEOVISUAL_DEFS_H
View
172 mythtv/libs/libmythtv/visualisations/videovisualspectrum.cpp
@@ -0,0 +1,172 @@
+#include <QPen>
+
+#include "videovisualspectrum.h"
+
+#define FFTW_N 512
+extern "C" {
+void *av_malloc(unsigned int size);
+void av_free(void *ptr);
+}
+
+VideoVisualSpectrum::VideoVisualSpectrum(AudioPlayer *audio, MythRender *render)
+ : VideoVisual(audio, render), m_range(1.0), m_scaleFactor(2.0),
+ m_falloff(3.0), m_barWidth(1)
+{
+ m_numSamples = 64;
+ lin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
+ rin = (myth_fftw_float*) av_malloc(sizeof(myth_fftw_float)*FFTW_N);
+ lout = (myth_fftw_complex*)
+ av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
+ rout = (myth_fftw_complex*)
+ av_malloc(sizeof(myth_fftw_complex)*(FFTW_N/2+1));
+
+ lplan = fftw_plan_dft_r2c_1d(FFTW_N, lin, (myth_fftw_complex_cast*)lout, FFTW_MEASURE);
+ rplan = fftw_plan_dft_r2c_1d(FFTW_N, rin, (myth_fftw_complex_cast*)rout, FFTW_MEASURE);
+}
+
+VideoVisualSpectrum::~VideoVisualSpectrum()
+{
+ if (lin)
+ av_free(lin);
+ if (rin)
+ av_free(rin);
+ if (lout)
+ av_free(lout);
+ if (rout)
+ av_free(rout);
+ fftw_destroy_plan(lplan);
+ fftw_destroy_plan(rplan);
+}
+
+template<typename T> T sq(T a) { return a*a; };
+
+void VideoVisualSpectrum::Draw(const QRect &area, MythPainter *painter,
+ QPaintDevice* device)
+{
+ if (m_disabled)
+ return;
+
+ mutex()->lock();
+ VisualNode *node = GetNode();
+
+ if (area.isEmpty() || !painter)
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ if (!Initialise(area))
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ uint i = 0;
+ if (node)
+ {
+ i = node->length;
+ fast_real_set_from_short(lin, node->left, node->length);
+ if (node->right)
+ fast_real_set_from_short(rin, node->right, node->length);
+ }
+ mutex()->unlock();
+
+ fast_reals_set(lin + i, rin + i, 0, FFTW_N - i);
+ fftw_execute(lplan);
+ fftw_execute(rplan);
+
+ double magL, magR, tmp;
+ double falloff = (((double)SetLastUpdate()) / 40.0) * m_falloff;
+ for (int l = 0, r = m_scale.range(); l < m_scale.range(); l++, r++)
+ {
+ int index = m_scale[l];
+ magL = (log(sq(real(lout[index])) + sq(real(lout[FFTW_N - index]))) - 22.0) *
+ m_scaleFactor;
+ magR = (log(sq(real(rout[index])) + sq(real(rout[FFTW_N - index]))) - 22.0) *
+ m_scaleFactor;
+
+ if (magL > m_range)
+ magL = 1.0;
+
+ if (magL < m_magnitudes[l])
+ {
+ tmp = m_magnitudes[l] - falloff;
+ if (tmp < magL)
+ tmp = magL;
+ magL = tmp;
+ }
+
+ if (magL < 1.0)
+ magL = 1.0;
+
+ if (magR > m_range)
+ magR = 1.0;
+
+ if (magR < m_magnitudes[r])
+ {
+ tmp = m_magnitudes[r] - falloff;
+ if (tmp < magR)
+ tmp = magR;
+ magR = tmp;
+ }
+
+ if (magR < 1.0)
+ magR = 1.0;
+
+ m_magnitudes[l] = magL;
+ m_magnitudes[r] = magR;
+ }
+
+ DrawPriv(painter, device);
+}
+
+void VideoVisualSpectrum::DrawPriv(MythPainter *painter, QPaintDevice* device)
+{
+ static const QBrush brush(QColor(0, 0, 200, 180));
+ static const QPen pen(QColor(255, 255, 255, 255));
+ double range = m_area.height() / 2.0;
+ int count = m_scale.range();
+ painter->Begin(device);
+ for (int i = 0; i < count; i++)
+ {
+ m_rects[i].setTop(range - int(m_magnitudes[i]));
+ m_rects[i].setBottom(range + int(m_magnitudes[i + count]));
+ if (m_rects[i].height() > 4)
+ painter->DrawRect(m_rects[i], brush, pen, 255);
+ }
+ painter->End();
+}
+
+bool VideoVisualSpectrum::Initialise(const QRect &area)
+{
+ if (area == m_area)
+ return true;
+
+ m_area = area;
+ m_barWidth = m_area.width() / m_numSamples;
+ if (m_barWidth < 6)
+ m_barWidth = 6;
+ m_scale.setMax(192, m_area.width() / m_barWidth);
+
+ m_magnitudes.resize(m_scale.range() * 2);
+ for (int i = 0; i < m_magnitudes.size(); i++)
+ m_magnitudes[i] = 0.0;
+
+ InitialisePriv();
+ return true;
+}
+
+bool VideoVisualSpectrum::InitialisePriv(void)
+{
+ m_range = m_area.height() / 2.0;
+ m_rects.resize(m_scale.range());
+ for (int i = 0, x = 0; i < m_rects.size(); i++, x+= m_barWidth)
+ m_rects[i].setRect(x, m_area.height() / 2, m_barWidth - 1, 1);
+
+ m_scaleFactor = double(m_area.height() / 2) / log((double)(FFTW_N));
+ m_falloff = (double)m_area.height() / 150.0;
+
+ VERBOSE(VB_GENERAL, DESC + QString("Initialised Spectrum with %1 bars")
+ .arg(m_scale.range()));
+ return true;
+}
View
40 mythtv/libs/libmythtv/visualisations/videovisualspectrum.h
@@ -0,0 +1,40 @@
+#ifndef VIDEOVISUALSPECTRUM_H
+#define VIDEOVISUALSPECTRUM_H
+
+#include <QVector>
+#include "videovisual.h"
+
+class VideoVisualSpectrum : public VideoVisual
+{
+ public:
+ VideoVisualSpectrum(AudioPlayer *audio, MythRender *render);
+ virtual ~VideoVisualSpectrum();
+
+ virtual void Draw(const QRect &area, MythPainter *painter,
+ QPaintDevice* device);
+
+ protected:
+ virtual bool Initialise(const QRect &area);
+ virtual bool InitialisePriv(void);
+ virtual void DrawPriv(MythPainter *painter, QPaintDevice* device);
+
+ int m_numSamples;
+ QVector<double> m_magnitudes;
+ double m_range;
+ LogScale m_scale;
+ double m_scaleFactor;
+ double m_falloff;
+
+ fftw_plan lplan;
+ fftw_plan rplan;
+ myth_fftw_float *lin;
+ myth_fftw_float *rin;
+ myth_fftw_complex *lout;
+ myth_fftw_complex *rout;
+
+ private:
+ QVector<QRect> m_rects;
+ int m_barWidth;
+};
+
+#endif // VIDEOVISUALSPECTRUM_H
Please sign in to comment.
Something went wrong with that request. Please try again.