strawberry-audio-player-win.../src/analyzer/analyzerbase.cpp

224 lines
5.8 KiB
C++
Executable File

/***************************************************************************
viswidget.cpp - description
-------------------
begin : Die Jan 7 2003
copyright : (C) 2003 by Max Howell
email : markey@web.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "config.h"
#include <cmath>
#include <QWidget>
#include <QPainter>
#include <QPalette>
#include <QTimerEvent>
#include <QtEvents>
#include "analyzerbase.h"
#include "engine/enginebase.h"
// INSTRUCTIONS Base2D
// 1. do anything that depends on height() in init(), Base2D will call it before
// you are shown
// 2. otherwise you can use the constructor to initialise things
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the
// widget when you return control to it
// 4. if you want to manipulate the scope, reimplement transform()
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
// TODO make an INSTRUCTIONS file
// can't mod scope in analyze you have to use transform
// TODO for 2D use setErasePixmap Qt function insetead of m_background
// make the linker happy only for gcc < 4.0
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
!defined(Q_OS_WIN32)
template class Analyzer::Base<QWidget>;
#endif
Analyzer::Base::Base(QWidget *parent, uint scopeSize)
: QWidget(parent),
m_timeout(40) // msec
,
m_fht(new FHT(scopeSize)),
m_engine(nullptr),
m_lastScope(512),
current_chunk_(0),
new_frame_(false),
is_playing_(false) {}
void Analyzer::Base::hideEvent(QHideEvent*) { m_timer.stop(); }
void Analyzer::Base::showEvent(QShowEvent*) { m_timer.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) // virtual
{
// this is a standard transformation that should give
// an FFT scope that has bands for pretty analyzers
// NOTE resizing here is redundant as FHT routines only calculate FHT::size()
// values
// scope.resize( m_fht->size() );
float *front = static_cast<float*>(&scope.front());
float *f = new float[m_fht->size()];
m_fht->copy(&f[0], front);
m_fht->logSpectrum(front, &f[0]);
m_fht->scale(front, 1.0 / 20);
scope.resize(m_fht->size() / 2); // second half of values are rubbish
delete[] f;
}
void Analyzer::Base::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window));
switch (m_engine->state()) {
case Engine::Playing: {
const Engine::Scope& thescope = m_engine->scope(m_timeout);
int i = 0;
// convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm
for (uint x = 0; (int)x < m_fht->size(); ++x) {
m_lastScope[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
i += 2;
}
is_playing_ = true;
transform(m_lastScope);
analyze(p, m_lastScope, new_frame_);
// scope.resize( m_fht->size() );
break;
}
case Engine::Paused:
is_playing_ = false;
analyze(p, m_lastScope, new_frame_);
break;
default:
is_playing_ = false;
demo(p);
}
new_frame_ = false;
}
int Analyzer::Base::resizeExponent(int exp) {
if (exp < 3)
exp = 3;
else if (exp > 9)
exp = 9;
if (exp != m_fht->sizeExp()) {
delete m_fht;
m_fht = new FHT(exp);
}
return exp;
}
int Analyzer::Base::resizeForBands(int bands) {
int exp;
if (bands <= 8)
exp = 4;
else if (bands <= 16)
exp = 5;
else if (bands <= 32)
exp = 6;
else if (bands <= 64)
exp = 7;
else if (bands <= 128)
exp = 8;
else
exp = 9;
resizeExponent(exp);
return m_fht->size() / 2;
}
void Analyzer::Base::demo(QPainter& p) // virtual
{
static int t = 201; // FIXME make static to namespace perhaps
if (t > 999) t = 1; // 0 = wasted calculations
if (t < 201) {
Scope s(32);
const double dt = double(t) / 200;
for (uint i = 0; i < s.size(); ++i)
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
analyze(p, s, new_frame_);
} else
analyze(p, Scope(32, 0), new_frame_);
++t;
}
void Analyzer::Base::polishEvent() {
init(); // virtual
}
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
double pos = 0.0;
const double step = (double)inVec.size() / outVec.size();
for (uint i = 0; i < outVec.size(); ++i, pos += step) {
const double error = pos - std::floor(pos);
const unsigned long offset = (unsigned long)pos;
unsigned long indexLeft = offset + 0;
if (indexLeft >= inVec.size()) indexLeft = inVec.size() - 1;
unsigned long indexRight = offset + 1;
if (indexRight >= inVec.size()) indexRight = inVec.size() - 1;
outVec[i] = inVec[indexLeft] * (1.0 - error) + inVec[indexRight] * error;
}
}
void Analyzer::initSin(Scope& v, const uint size) {
double step = (M_PI * 2) / size;
double radian = 0;
for (uint i = 0; i < size; i++) {
v.push_back(sin(radian));
radian += step;
}
}
void Analyzer::Base::timerEvent(QTimerEvent *e) {
QWidget::timerEvent(e);
if (e->timerId() != m_timer.timerId()) return;
new_frame_ = true;
update();
}