Add "Psychedelic Colour" mode to all analyzers

(Well, except Nyanalyzer and Rainbow dash because they are already colourful enough.)

I have added functionality for any 2D analyzer to change any part of its colour palatte with the frequency content of the music, in the same way that Moodbars do.
I find this gives the analyzer a sort of "third dimention".
This is built into Analyzer::Base, so all analyzers can use it and override it as they please. I have thus added support for Block, Boom, Turbine, Sonogram, and Bar, however Boom and Block seem to look the best in my opinion.
This is of course all optional and is toggled by a checkbox in the context menu for the analyzer, disabled by default.
I have not been able to measure any increase in CPU activity with this enabled, even at 60fps.
This commit is contained in:
Mark Furneaux 2015-07-01 11:48:03 -04:00
parent 73ae416534
commit c102bf7fe6
18 changed files with 660 additions and 417 deletions

View File

@ -2,7 +2,7 @@
Copyright 2003, Max Howell <max.howell@methylblue.com>
Copyright 2009, 2011-2012, David Sansome <me@davidsansome.com>
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -33,6 +33,7 @@
#include <QtDebug>
#include "engines/enginebase.h"
#include "core/arraysize.h"
// INSTRUCTIONS Base2D
// 1. do anything that depends on height() in init(), Base2D will call it before
@ -45,7 +46,8 @@
// TODO(David Sansome): make an INSTRUCTIONS file
// can't mod scope in analyze you have to use transform
// TODO(John Maguire): for 2D use setErasePixmap Qt function insetead of m_background
// TODO(John Maguire): 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)) && \
@ -53,20 +55,29 @@
template class Analyzer::Base<QWidget>;
#endif
static const int sBarkBands[] = {
100, 200, 300, 400, 510, 630, 770, 920, 1080, 1270, 1480, 1720,
2000, 2320, 2700, 3150, 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500};
static const int sBarkBandCount = arraysize(sBarkBands);
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
: QWidget(parent),
m_timeout(40) // msec
timeout_(40) // msec
,
m_fht(new FHT(scopeSize)),
m_engine(nullptr),
m_lastScope(512),
current_chunk_(0),
fht_(new FHT(scopeSize)),
engine_(nullptr),
lastScope_(512),
new_frame_(false),
is_playing_(false) {}
is_playing_(false),
barkband_table_(QList<uint>()),
prev_color_index_(0),
bands_(0),
psychedelic_enabled_(false) {}
void Analyzer::Base::hideEvent(QHideEvent*) { m_timer.stop(); }
void Analyzer::Base::hideEvent(QHideEvent*) { timer_.stop(); }
void Analyzer::Base::showEvent(QShowEvent*) { m_timer.start(timeout(), this); }
void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) {
// this is a standard transformation that should give
@ -74,16 +85,16 @@ void Analyzer::Base::transform(Scope& scope) {
// NOTE resizing here is redundant as FHT routines only calculate FHT::size()
// values
// scope.resize( m_fht->size() );
// scope.resize( 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);
float* f = new float[fht_->size()];
fht_->copy(&f[0], front);
fht_->logSpectrum(front, &f[0]);
fht_->scale(front, 1.0 / 20);
scope.resize(m_fht->size() / 2); // second half of values are rubbish
scope.resize(fht_->size() / 2); // second half of values are rubbish
delete[] f;
}
@ -91,30 +102,30 @@ void Analyzer::Base::paintEvent(QPaintEvent* e) {
QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window));
switch (m_engine->state()) {
switch (engine_->state()) {
case Engine::Playing: {
const Engine::Scope& thescope = m_engine->scope(m_timeout);
const Engine::Scope& thescope = engine_->scope(timeout_);
int i = 0;
// convert to mono here - our built in analyzers need mono, but the
// engines provide interleaved pcm
for (uint x = 0; static_cast<int>(x) < m_fht->size(); ++x) {
m_lastScope[x] =
static_cast<double>(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
for (uint x = 0; static_cast<int>(x) < fht_->size(); ++x) {
lastScope_[x] = static_cast<double>(thescope[i] + thescope[i + 1]) /
(2 * (1 << 15));
i += 2;
}
is_playing_ = true;
transform(m_lastScope);
analyze(p, m_lastScope, new_frame_);
transform(lastScope_);
analyze(p, lastScope_, new_frame_);
// scope.resize( m_fht->size() );
// scope.resize( fht_->size() );
break;
}
case Engine::Paused:
is_playing_ = false;
analyze(p, m_lastScope, new_frame_);
analyze(p, lastScope_, new_frame_);
break;
default:
@ -131,9 +142,9 @@ int Analyzer::Base::resizeExponent(int exp) {
else if (exp > 9)
exp = 9;
if (exp != m_fht->sizeExp()) {
delete m_fht;
m_fht = new FHT(exp);
if (exp != fht_->sizeExp()) {
delete fht_;
fht_ = new FHT(exp);
}
return exp;
}
@ -154,7 +165,7 @@ int Analyzer::Base::resizeForBands(int bands) {
exp = 9;
resizeExponent(exp);
return m_fht->size() / 2;
return fht_->size() / 2;
}
void Analyzer::Base::demo(QPainter& p) {
@ -175,6 +186,67 @@ void Analyzer::Base::demo(QPainter& p) {
++t;
}
void Analyzer::Base::psychedelicModeChanged(bool enabled) {
psychedelic_enabled_ = enabled;
}
int Analyzer::Base::BandFrequency(int band) const {
return ((kSampleRate / 2) * band + kSampleRate / 4) / bands_;
}
void Analyzer::Base::updateBandSize(const int scopeSize) {
// prevent possible dbz in BandFrequency
if (scopeSize == 0) {
return;
}
bands_ = scopeSize;
barkband_table_.clear();
barkband_table_.reserve(bands_ + 1);
int barkband = 0;
for (int i = 0; i < bands_ + 1; ++i) {
if (barkband < sBarkBandCount - 1 &&
BandFrequency(i) >= sBarkBands[barkband]) {
barkband++;
}
barkband_table_.append(barkband);
}
}
QColor Analyzer::Base::getPsychedelicColor(const Scope& scope,
const int ampFactor,
const int bias) {
if (scope.size() > barkband_table_.length()) {
return palette().color(QPalette::Highlight);
}
// Calculate total magnitudes for different bark bands.
double bands[sBarkBandCount]{};
for (int i = 0; i < barkband_table_.size(); ++i) {
bands[barkband_table_[i]] += scope[i];
}
// Now divide the bark bands into thirds and compute their total amplitudes.
double rgb[3]{};
for (int i = 0; i < sBarkBandCount - 1; ++i) {
rgb[(i * 3) / sBarkBandCount] += bands[i] * bands[i];
}
for (int i = 0; i < 3; ++i) {
// bias colours for a threshold around normally amplified audio
rgb[i] = (int)((sqrt(rgb[i]) * ampFactor) + bias);
if (rgb[i] > 255) {
rgb[i] = 255;
}
}
return QColor::fromRgb(rgb[0], rgb[1], rgb[2]);
}
void Analyzer::Base::polishEvent() {
init(); // virtual
}
@ -211,7 +283,7 @@ void Analyzer::initSin(Scope& v, const uint size) {
void Analyzer::Base::timerEvent(QTimerEvent* e) {
QWidget::timerEvent(e);
if (e->timerId() != m_timer.timerId()) return;
if (e->timerId() != timer_.timerId()) return;
new_frame_ = true;
update();

View File

@ -3,7 +3,7 @@
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -58,21 +58,22 @@ class Base : public QWidget {
Q_OBJECT
public:
~Base() { delete m_fht; }
~Base() { delete fht_; }
uint timeout() const { return m_timeout; }
uint timeout() const { return timeout_; }
void set_engine(EngineBase* engine) { m_engine = engine; }
void set_engine(EngineBase* engine) { engine_ = engine; }
void changeTimeout(uint newTimeout) {
m_timeout = newTimeout;
if (m_timer.isActive()) {
m_timer.stop();
m_timer.start(m_timeout, this);
timeout_ = newTimeout;
if (timer_.isActive()) {
timer_.stop();
timer_.start(timeout_, this);
}
}
virtual void framerateChanged() {}
virtual void psychedelicModeChanged(bool);
protected:
explicit Base(QWidget*, uint scopeSize = 7);
@ -86,21 +87,32 @@ class Base : public QWidget {
int resizeExponent(int);
int resizeForBands(int);
int BandFrequency(int) const;
void updateBandSize(const int);
QColor getPsychedelicColor(const Scope&, const int, const int);
virtual void init() {}
virtual void transform(Scope&);
virtual void analyze(QPainter& p, const Scope&, bool new_frame) = 0;
virtual void demo(QPainter& p);
protected:
QBasicTimer m_timer;
uint m_timeout;
FHT* m_fht;
EngineBase* m_engine;
Scope m_lastScope;
int current_chunk_;
static const int kSampleRate =
44100; // we shouldn't need to care about ultrasonics
QBasicTimer timer_;
uint timeout_;
FHT* fht_;
EngineBase* engine_;
Scope lastScope_;
bool new_frame_;
bool is_playing_;
QList<uint> barkband_table_;
double prev_colors_[10][3];
int prev_color_index_;
int bands_;
bool psychedelic_enabled_;
};
void interpolate(const Scope&, Scope&);

View File

@ -3,7 +3,7 @@
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2011-2012, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2013, Vasily Fomin <vasili.fomin@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -57,6 +57,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
visualisation_action_(nullptr),
double_click_timer_(new QTimer(this)),
ignore_next_click_(false),
psychedelic_colors_on_(false),
current_analyzer_(nullptr),
engine_(nullptr) {
QHBoxLayout* layout = new QHBoxLayout(this);
@ -88,6 +89,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
disable_action_->setCheckable(true);
group_->addAction(disable_action_);
context_menu_->addSeparator();
psychedelic_enable_ = context_menu_->addAction(
tr("Use Psychedelic Colors"), this, SLOT(TogglePsychedelicColors()));
psychedelic_enable_->setCheckable(true);
context_menu_->addSeparator();
// Visualisation action gets added in SetActions
@ -145,6 +151,12 @@ void AnalyzerContainer::DisableAnalyzer() {
Save();
}
void AnalyzerContainer::TogglePsychedelicColors() {
psychedelic_colors_on_ ^= true;
current_analyzer_->psychedelicModeChanged(psychedelic_colors_on_);
SavePsychedelic();
}
void AnalyzerContainer::ChangeAnalyzer(int id) {
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
@ -161,6 +173,7 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
current_framerate_ =
current_framerate_ == 0 ? kMediumFramerate : current_framerate_;
current_analyzer_->changeTimeout(1000 / current_framerate_);
current_analyzer_->psychedelicModeChanged(psychedelic_colors_on_);
layout()->addWidget(current_analyzer_);
@ -183,6 +196,10 @@ void AnalyzerContainer::Load() {
QSettings s;
s.beginGroup(kSettingsGroup);
// Colours
psychedelic_colors_on_ = s.value("psychedelic", false).toBool();
psychedelic_enable_->setChecked(psychedelic_colors_on_);
// Analyzer
QString type = s.value("type", "BlockAnalyzer").toString();
if (type.isEmpty()) {
@ -227,6 +244,13 @@ void AnalyzerContainer::Save() {
: QVariant());
}
void AnalyzerContainer::SavePsychedelic() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("psychedelic", psychedelic_colors_on_);
}
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
QAction* action =
context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));

View File

@ -4,6 +4,7 @@
Copyright 2011-2012, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2013, Vasily Fomin <vasili.fomin@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2015, Mark Furneaux <mark@furneaux.ca>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -40,7 +41,7 @@ class AnalyzerContainer : public QWidget {
static const char* kSettingsGroup;
static const char* kSettingsFramerate;
signals:
signals:
void WheelEvent(int delta);
protected:
@ -53,6 +54,7 @@ class AnalyzerContainer : public QWidget {
void ChangeFramerate(int new_framerate);
void DisableAnalyzer();
void ShowPopupMenu();
void TogglePsychedelicColors();
private:
static const int kLowFramerate;
@ -63,6 +65,7 @@ class AnalyzerContainer : public QWidget {
void Load();
void Save();
void SaveFramerate(int framerate);
void SavePsychedelic();
template <typename T>
void AddAnalyzerType();
void AddFramerate(const QString& name, int framerate);
@ -80,11 +83,13 @@ class AnalyzerContainer : public QWidget {
QList<int> framerate_list_;
QList<QAction*> actions_;
QAction* disable_action_;
QAction* psychedelic_enable_;
QAction* visualisation_action_;
QTimer* double_click_timer_;
QPoint last_click_pos_;
bool ignore_next_click_;
bool psychedelic_colors_on_;
Analyzer::Base* current_analyzer_;
EngineBase* engine_;

View File

@ -2,7 +2,7 @@
Copyright 2003, Mark Kretschmann <markey@web.de>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
@ -23,7 +23,6 @@
/* Original Author: Mark Kretschmann <markey@web.de> 2003
*/
#include "baranalyzer.h"
#include <cmath>
#include <QtDebug>
@ -36,19 +35,20 @@ const char* BarAnalyzer::kName =
BarAnalyzer::BarAnalyzer(QWidget* parent) : Analyzer::Base(parent, 8) {
// roof pixmaps don't depend on size() so we do in the ctor
m_bg = parent->palette().color(QPalette::Background);
bg_ = parent->palette().color(QPalette::Background);
QColor fg(parent->palette().color(QPalette::Highlight).lighter(150));
double dr = static_cast<double>(m_bg.red() - fg.red()) /
(NUM_ROOFS - 1); // -1 because we start loop below at 0
double dg = static_cast<double>(m_bg.green() - fg.green()) / (NUM_ROOFS - 1);
double db = static_cast<double>(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1);
double dr = static_cast<double>(bg_.red() - fg.red()) /
(kNumRoofs - 1); // -1 because we start loop below at 0
double dg = static_cast<double>(bg_.green() - fg.green()) / (kNumRoofs - 1);
double db = static_cast<double>(bg_.blue() - fg.blue()) / (kNumRoofs - 1);
for (uint i = 0; i < NUM_ROOFS; ++i) {
m_pixRoof[i] = QPixmap(COLUMN_WIDTH, 1);
m_pixRoof[i].fill(QColor(fg.red() + static_cast<int>(dr * i), fg.green() + static_cast<int>(dg * i),
fg.blue() + static_cast<int>(db * i)));
for (uint i = 0; i < kNumRoofs; ++i) {
pixRoof_[i] = QPixmap(kColumnWidth, 1);
pixRoof_[i].fill(QColor(fg.red() + static_cast<int>(dr * i),
fg.green() + static_cast<int>(dg * i),
fg.blue() + static_cast<int>(db * i)));
}
}
@ -58,46 +58,66 @@ void BarAnalyzer::resizeEvent(QResizeEvent* e) { init(); }
void BarAnalyzer::init() {
const double MAX_AMPLITUDE = 1.0;
const double F = static_cast<double>(height() - 2) / (log10(255) * MAX_AMPLITUDE);
const double F =
static_cast<double>(height() - 2) / (log10(255) * MAX_AMPLITUDE);
BAND_COUNT = width() / 5;
MAX_DOWN = static_cast<int>(0 - (qMax(1, height() / 50)));
MAX_UP = static_cast<int>(qMax(1, height() / 25));
band_count_ = width() / 5;
max_down_ = static_cast<int>(0 - (qMax(1, height() / 50)));
max_up_ = static_cast<int>(qMax(1, height() / 25));
barVector.resize(BAND_COUNT, 0);
roofVector.resize(BAND_COUNT, height() - 5);
roofVelocityVector.resize(BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR);
m_roofMem.resize(BAND_COUNT);
m_scope.resize(BAND_COUNT);
barVector_.resize(band_count_, 0);
roofVector_.resize(band_count_, height() - 5);
roofVelocityVector_.resize(band_count_, kRoofVelocityReductionFactor);
roofMem_.resize(band_count_);
scope_.resize(band_count_);
// generate a list of values that express amplitudes in range 0-MAX_AMP as
// ints from 0-height() on log scale
for (uint x = 0; x < 256; ++x) {
m_lvlMapper[x] = static_cast<uint>(F * log10(x + 1));
lvlMapper_[x] = static_cast<uint>(F * log10(x + 1));
}
m_pixBarGradient = QPixmap(height() * COLUMN_WIDTH, height());
m_pixCompose = QPixmap(size());
pixBarGradient_ = QPixmap(height() * kColumnWidth, height());
pixCompose_ = QPixmap(size());
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
QPainter p(&m_pixBarGradient);
QColor rgb(palette().color(QPalette::Highlight));
updateBandSize(band_count_);
colorChanged();
setMinimumSize(QSize(band_count_ * kColumnWidth, 10));
}
for (int x = 0, r = rgb.red(), g = rgb.green(), b = rgb.blue(), r2 = 255 - r; x < height();
++x) {
void BarAnalyzer::colorChanged() {
if (pixBarGradient_.isNull()) {
return;
}
QPainter p(&pixBarGradient_);
QColor rgb;
if (psychedelic_enabled_) {
rgb = getPsychedelicColor(scope_, 50, 100);
} else {
rgb = palette().color(QPalette::Highlight);
}
for (int x = 0, r = rgb.red(), g = rgb.green(), b = rgb.blue(), r2 = 255 - r;
x < height(); ++x) {
for (int y = x; y > 0; --y) {
const double fraction = static_cast<double>(y) / height();
// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 *
// fraction) ) );
p.setPen(QColor(r + static_cast<int>(r2 * fraction), g, b));
p.drawLine(x * COLUMN_WIDTH, height() - y, (x + 1) * COLUMN_WIDTH,
p.drawLine(x * kColumnWidth, height() - y, (x + 1) * kColumnWidth,
height() - y);
}
}
}
setMinimumSize(QSize(BAND_COUNT * COLUMN_WIDTH, 10));
void BarAnalyzer::psychedelicModeChanged(bool enabled) {
psychedelic_enabled_ = enabled;
// reset colours back to normal
colorChanged();
}
void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
@ -107,20 +127,25 @@ void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
}
// Analyzer::interpolate( s, m_bands );
Scope& v = m_scope;
Analyzer::interpolate(s, v);
Analyzer::interpolate(s, scope_);
QPainter canvas_painter(&canvas_);
// update the graphics with the new colour
if (psychedelic_enabled_) {
colorChanged();
}
canvas_.fill(palette().color(QPalette::Background));
for (uint i = 0, x = 0, y2; i < v.size(); ++i, x += COLUMN_WIDTH + 1) {
for (uint i = 0, x = 0, y2; i < scope_.size(); ++i, x += kColumnWidth + 1) {
// assign pre[log10]'d value
y2 = static_cast<uint>(v[i] *
256); // 256 will be optimised to a bitshift //no, it's a float
y2 = m_lvlMapper[(y2 > 255) ? 255 : y2]; // lvlMapper is array of ints with
// values 0 to height()
y2 = static_cast<uint>(
scope_[i] *
256); // 256 will be optimised to a bitshift //no, it's a float
y2 = lvlMapper_[(y2 > 255) ? 255 : y2]; // lvlMapper is array of ints with
// values 0 to height()
int change = y2 - barVector[i];
int change = y2 - barVector_[i];
// using the best of Markey's, piggz and Max's ideas on the way to shift the
// bars
@ -129,54 +154,52 @@ void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
// 2. shift large upwards with a bias towards last value
// 3. fall downwards at a constant pace
/*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter"
/*if ( change > max_up_ ) //anything too much greater than 2 gives "jitter"
//add some dynamics - makes the value slightly closer to what it was last time
y2 = ( barVector[i] + MAX_UP );
//y2 = ( barVector[i] * 2 + y2 ) / 3;
y2 = ( barVector_[i] + max_up_ );
//y2 = ( barVector_[i] * 2 + y2 ) / 3;
else*/ if (change <
MAX_DOWN)
y2 = barVector[i] + MAX_DOWN;
max_down_)
y2 = barVector_[i] + max_down_;
if (static_cast<int>(y2) > roofVector[i]) {
roofVector[i] = static_cast<int>(y2);
roofVelocityVector[i] = 1;
if (static_cast<int>(y2) > roofVector_[i]) {
roofVector_[i] = static_cast<int>(y2);
roofVelocityVector_[i] = 1;
}
// remember where we are
barVector[i] = y2;
barVector_[i] = y2;
if (m_roofMem[i].size() > NUM_ROOFS)
m_roofMem[i].erase(m_roofMem[i].begin());
if (roofMem_[i].size() > kNumRoofs) roofMem_[i].erase(roofMem_[i].begin());
// blt last n roofs, a.k.a motion blur
for (uint c = 0; c < m_roofMem[i].size(); ++c)
// bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ]
for (uint c = 0; c < roofMem_[i].size(); ++c)
// bitBlt( m_pComposePixmap, x, roofMem_[i]->at( c ), m_roofPixmaps[ c ]
// );
// bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ]
// bitBlt( canvas(), x, roofMem_[i][c], &pixRoof_[ kNumRoofs - 1 - c ]
// );
canvas_painter.drawPixmap(x, m_roofMem[i][c],
m_pixRoof[NUM_ROOFS - 1 - c]);
canvas_painter.drawPixmap(x, roofMem_[i][c], pixRoof_[kNumRoofs - 1 - c]);
// blt the bar
canvas_painter.drawPixmap(x, height() - y2, *gradient(), y2 * COLUMN_WIDTH,
height() - y2, COLUMN_WIDTH, y2);
canvas_painter.drawPixmap(x, height() - y2, *gradient(), y2 * kColumnWidth,
height() - y2, kColumnWidth, y2);
/*bitBlt( canvas(), x, height() - y2,
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2,
gradient(), y2 * kColumnWidth, height() - y2, kColumnWidth, y2,
Qt::CopyROP );*/
m_roofMem[i].push_back(height() - roofVector[i] - 2);
roofMem_[i].push_back(height() - roofVector_[i] - 2);
// set roof parameters for the NEXT draw
if (roofVelocityVector[i] != 0) {
if (roofVelocityVector[i] > 32) // no reason to do == 32
roofVector[i] -=
(roofVelocityVector[i] - 32) / 20; // trivial calculation
if (roofVelocityVector_[i] != 0) {
if (roofVelocityVector_[i] > 32) // no reason to do == 32
roofVector_[i] -=
(roofVelocityVector_[i] - 32) / 20; // trivial calculation
if (roofVector[i] < 0) {
roofVector[i] = 0; // not strictly necessary
roofVelocityVector[i] = 0;
if (roofVector_[i] < 0) {
roofVector_[i] = 0; // not strictly necessary
roofVelocityVector_[i] = 0;
} else {
++roofVelocityVector[i];
++roofVelocityVector_[i];
}
}
}

View File

@ -2,7 +2,7 @@
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
Copyright 2005, Mark Kretschmann <markey@web.de>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
@ -39,44 +39,45 @@ class BarAnalyzer : public Analyzer::Base {
void init();
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
// virtual void transform( Scope& );
virtual void psychedelicModeChanged(bool);
/**
* Resizes the widget to a new geometry according to @p e
* @param e The resize-event
*/
void resizeEvent(QResizeEvent* e);
void colorChanged();
uint BAND_COUNT;
int MAX_DOWN;
int MAX_UP;
static const uint ROOF_HOLD_TIME = 48;
static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32;
static const uint NUM_ROOFS = 16;
static const uint COLUMN_WIDTH = 4;
uint band_count_;
int max_down_;
int max_up_;
static const uint kRoofHoldTime = 48;
static const int kRoofVelocityReductionFactor = 32;
static const uint kNumRoofs = 16;
static const uint kColumnWidth = 4;
static const char* kName;
protected:
QPixmap m_pixRoof[NUM_ROOFS];
// vector<uint> m_roofMem[BAND_COUNT];
QPixmap pixRoof_[kNumRoofs];
// vector<uint> roofMem_[band_count_];
// Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope
// every iteration
uint m_lvlMapper[256];
std::vector<aroofMemVec> m_roofMem;
std::vector<uint> barVector; // positions of bars
std::vector<int> roofVector; // positions of roofs
std::vector<uint> roofVelocityVector; // speed that roofs falls
uint lvlMapper_[256];
std::vector<aroofMemVec> roofMem_;
std::vector<uint> barVector_; // positions of bars
std::vector<int> roofVector_; // positions of roofs
std::vector<uint> roofVelocityVector_; // speed that roofs falls
const QPixmap* gradient() const { return &m_pixBarGradient; }
const QPixmap* gradient() const { return &pixBarGradient_; }
private:
QPixmap m_pixBarGradient;
QPixmap m_pixCompose;
QPixmap pixBarGradient_;
QPixmap pixCompose_;
QPixmap canvas_;
Analyzer::Scope m_scope; // so we don't create a vector every frame
QColor m_bg;
Analyzer::Scope scope_; // so we don't create a vector every frame
QColor bg_;
};
#endif // ANALYZERS_BARANALYZER_H_

View File

@ -3,7 +3,7 @@
Copyright 2005, Mark Kretschmann <markey@web.de>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -33,35 +33,34 @@
#include <cstdlib>
#include <QPainter>
const uint BlockAnalyzer::HEIGHT = 2;
const uint BlockAnalyzer::WIDTH = 4;
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
const uint BlockAnalyzer::FADE_SIZE = 90;
const uint BlockAnalyzer::kHeight = 2;
const uint BlockAnalyzer::kWidth = 4;
const uint BlockAnalyzer::kMinRows = 3; // arbituary
const uint BlockAnalyzer::kMinColumns = 32; // arbituary
const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const uint BlockAnalyzer::kFadeSize = 90;
const char* BlockAnalyzer::kName =
QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
m_columns(0),
m_rows(0),
m_y(0),
m_barPixmap(1, 1),
m_topBarPixmap(WIDTH, HEIGHT),
m_scope(MIN_COLUMNS),
m_store(1 << 8, 0),
m_fade_bars(FADE_SIZE),
m_fade_pos(1 << 8, 50),
m_fade_intensity(1 << 8, 32) {
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1,
MIN_ROWS * (HEIGHT + 1) - 1);
columns_(0),
rows_(0),
y_(0),
barPixmap_(1, 1),
topBarPixmap_(kWidth, kHeight),
scope_(kMinColumns),
store_(1 << 8, 0),
fade_bars_(kFadeSize),
fade_pos_(1 << 8, 50),
fade_intensity_(1 << 8, 32) {
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1);
// -1 is padding, no drawing takes place there
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
// mxcl says null pixmaps cause crashes, so let's play it safe
for (uint i = 0; i < FADE_SIZE; ++i) m_fade_bars[i] = QPixmap(1, 1);
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
}
BlockAnalyzer::~BlockAnalyzer() {}
@ -69,41 +68,44 @@ BlockAnalyzer::~BlockAnalyzer() {}
void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
m_background = QPixmap(size());
background_ = QPixmap(size());
canvas_ = QPixmap(size());
const uint oldRows = m_rows;
const uint oldRows = rows_;
// all is explained in analyze()..
// +1 to counter -1 in maxSizes, trust me we need this!
m_columns = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (WIDTH + 1)) + 1, MAX_COLUMNS);
m_rows = static_cast<uint>(static_cast<double>(height() + 1) / (HEIGHT + 1));
columns_ = qMin(
static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1,
kMaxColumns);
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
// this is the y-offset for drawing from the top of the widget
m_y = (height() - (m_rows * (HEIGHT + 1)) + 2) / 2;
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
m_scope.resize(m_columns);
scope_.resize(columns_);
if (m_rows != oldRows) {
m_barPixmap = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
if (rows_ != oldRows) {
barPixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
for (uint i = 0; i < FADE_SIZE; ++i)
m_fade_bars[i] = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
for (uint i = 0; i < kFadeSize; ++i)
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
m_yscale.resize(m_rows + 1);
yscale_.resize(rows_ + 1);
const uint PRE = 1,
PRO = 1; // PRE and PRO allow us to restrict the range somewhat
for (uint z = 0; z < m_rows; ++z)
m_yscale[z] = 1 - (log10(PRE + z) / log10(PRE + m_rows + PRO));
for (uint z = 0; z < rows_; ++z)
yscale_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
m_yscale[m_rows] = 0;
yscale_[rows_] = 0;
determineStep();
paletteChange(palette());
}
updateBandSize(columns_);
drawBackground();
}
@ -113,9 +115,9 @@ void BlockAnalyzer::determineStep() {
// I calculated the value 30 based on some trial and error
// the fall time of 30 is too slow on framerates above 50fps
const double fallTime = timeout() < 20 ? 20 * m_rows : 30 * m_rows;
const double fallTime = timeout() < 20 ? 20 * rows_ : 30 * rows_;
m_step = static_cast<double>(m_rows * timeout()) / fallTime;
step_ = static_cast<double>(rows_ * timeout()) / fallTime;
}
void BlockAnalyzer::framerateChanged() { // virtual
@ -127,15 +129,14 @@ void BlockAnalyzer::transform(Analyzer::Scope& s) {
float* front = static_cast<float*>(&s.front());
m_fht->spectrum(front);
m_fht->scale(front, 1.0 / 20);
fht_->spectrum(front);
fht_->scale(front, 1.0 / 20);
// the second half is pretty dull, so only show it if the user has a large
// analyzer
// by setting to m_scope.size() if large we prevent interpolation of large
// by setting to scope_.size() if large we prevent interpolation of large
// analyzers, this is good!
s.resize(m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2
: m_scope.size());
s.resize(scope_.size() <= kMaxColumns / 2 ? kMaxColumns / 2 : scope_.size());
}
void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
@ -150,7 +151,7 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
// y represents the number of blanks
// y starts from the top and increases in units of blocks
// m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
// yscale_ looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
// if it contains 6 elements there are 5 rows in the analyzer
if (!new_frame) {
@ -160,51 +161,55 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
QPainter canvas_painter(&canvas_);
Analyzer::interpolate(s, m_scope);
Analyzer::interpolate(s, scope_);
// update the graphics with the new colour
if (psychedelic_enabled_) {
paletteChange(QPalette());
}
// Paint the background
canvas_painter.drawPixmap(0, 0, m_background);
canvas_painter.drawPixmap(0, 0, background_);
for (uint y, x = 0; x < m_scope.size(); ++x) {
for (uint y, x = 0; x < scope_.size(); ++x) {
// determine y
for (y = 0; m_scope[x] < m_yscale[y]; ++y)
continue;
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
// this is opposite to what you'd think, higher than y
// means the bar is lower than y (physically)
if (static_cast<float>(y) > m_store[x])
y = static_cast<int>(m_store[x] += m_step);
if (static_cast<float>(y) > store_[x])
y = static_cast<int>(store_[x] += step_);
else
m_store[x] = y;
store_[x] = y;
// if y is lower than m_fade_pos, then the bar has exceeded the height of
// if y is lower than fade_pos_, then the bar has exceeded the kHeight of
// the fadeout
// if the fadeout is quite faded now, then display the new one
if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
m_fade_pos[x] = y;
m_fade_intensity[x] = FADE_SIZE;
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
fade_pos_[x] = y;
fade_intensity_[x] = kFadeSize;
}
if (m_fade_intensity[x] > 0) {
const uint offset = --m_fade_intensity[x];
const uint y = m_y + (m_fade_pos[x] * (HEIGHT + 1));
canvas_painter.drawPixmap(x * (WIDTH + 1), y, m_fade_bars[offset], 0, 0,
WIDTH, height() - y);
if (fade_intensity_[x] > 0) {
const uint offset = --fade_intensity_[x];
const uint y = y_ + (fade_pos_[x] * (kHeight + 1));
canvas_painter.drawPixmap(x * (kWidth + 1), y, fade_bars_[offset], 0, 0,
kWidth, height() - y);
}
if (m_fade_intensity[x] == 0) m_fade_pos[x] = m_rows;
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing,
// m_rows means none are
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(),
0, y * (HEIGHT + 1), bar()->width(),
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing,
// rows_ means none are
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(),
0, y * (kHeight + 1), bar()->width(),
bar()->height());
}
for (uint x = 0; x < m_store.size(); ++x)
canvas_painter.drawPixmap(x * (WIDTH + 1),
static_cast<int>(m_store[x]) * (HEIGHT + 1) + m_y,
m_topBarPixmap);
for (uint x = 0; x < store_.size(); ++x)
canvas_painter.drawPixmap(x * (kWidth + 1),
static_cast<int>(store_[x]) * (kHeight + 1) + y_,
topBarPixmap_);
p.drawPixmap(0, 0, canvas_);
}
@ -232,6 +237,12 @@ static inline void adjustToLimits(int& b, int& f, uint& amount) {
}
}
void BlockAnalyzer::psychedelicModeChanged(bool enabled) {
psychedelic_enabled_ = enabled;
// reset colours back to normal
paletteChange(QPalette());
}
/**
* Clever contrast function
*
@ -305,7 +316,8 @@ QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
if (static_cast<int>(_amount) > 0) adjustToLimits(bs, fs, _amount);
// see if we need to adjust the hue
if (static_cast<int>(_amount) > 0) fh += static_cast<int>(_amount); // cycles around;
if (static_cast<int>(_amount) > 0)
fh += static_cast<int>(_amount); // cycles around;
return QColor::fromHsv(fh, fs, fv);
}
@ -327,23 +339,34 @@ QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
void BlockAnalyzer::paletteChange(const QPalette&) {
const QColor bg = palette().color(QPalette::Background);
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
QColor fg;
m_topBarPixmap.fill(fg);
if (psychedelic_enabled_) {
fg = getPsychedelicColor(scope_, 10, 75);
} else {
fg = ensureContrast(bg, palette().color(QPalette::Highlight));
}
const double dr = 15 * static_cast<double>(bg.red() - fg.red()) / (m_rows * 16);
const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (m_rows * 16);
const double db = 15 * static_cast<double>(bg.blue() - fg.blue()) / (m_rows * 16);
topBarPixmap_.fill(fg);
const double dr =
15 * static_cast<double>(bg.red() - fg.red()) / (rows_ * 16);
const double dg =
15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
const double db =
15 * static_cast<double>(bg.blue() - fg.blue()) / (rows_ * 16);
const int r = fg.red(), g = fg.green(), b = fg.blue();
bar()->fill(bg);
QPainter p(bar());
for (int y = 0; static_cast<uint>(y) < m_rows; ++y)
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
// graduate the fg color
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT,
QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y),
b + static_cast<int>(db * y)));
p.fillRect(
0, y * (kHeight + 1), kWidth, kHeight,
QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y),
b + static_cast<int>(db * y)));
{
const QColor bg = palette().color(QPalette::Background).dark(112);
@ -360,13 +383,15 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
const int r = bg.red(), g = bg.green(), b = bg.blue();
// Precalculate all fade-bar pixmaps
for (uint y = 0; y < FADE_SIZE; ++y) {
m_fade_bars[y].fill(palette().color(QPalette::Background));
QPainter f(&m_fade_bars[y]);
for (int z = 0; static_cast<uint>(z) < m_rows; ++z) {
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT,
QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y), b + static_cast<int>(db * Y)));
for (uint y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Background));
QPainter f(&fade_bars_[y]);
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
f.fillRect(
0, z * (kHeight + 1), kWidth, kHeight,
QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y),
b + static_cast<int>(db * Y)));
}
}
}
@ -378,11 +403,16 @@ void BlockAnalyzer::drawBackground() {
const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark(112);
m_background.fill(bg);
background_.fill(bg);
QPainter p(&m_background);
for (int x = 0; (uint)x < m_columns; ++x)
for (int y = 0; (uint)y < m_rows; ++y)
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, WIDTH, HEIGHT,
QPainter p(&background_);
if (p.paintEngine() == 0) {
return;
}
for (int x = 0; (uint)x < columns_; ++x)
for (int y = 0; (uint)y < rows_; ++y)
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight,
bgdark);
}

View File

@ -2,7 +2,7 @@
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -39,12 +39,12 @@ class BlockAnalyzer : public Analyzer::Base {
Q_INVOKABLE BlockAnalyzer(QWidget*);
~BlockAnalyzer();
static const uint HEIGHT;
static const uint WIDTH;
static const uint MIN_ROWS;
static const uint MIN_COLUMNS;
static const uint MAX_COLUMNS;
static const uint FADE_SIZE;
static const uint kHeight;
static const uint kWidth;
static const uint kMinRows;
static const uint kMinColumns;
static const uint kMaxColumns;
static const uint kFadeSize;
static const char* kName;
@ -54,29 +54,30 @@ class BlockAnalyzer : public Analyzer::Base {
virtual void resizeEvent(QResizeEvent*);
virtual void paletteChange(const QPalette&);
virtual void framerateChanged();
virtual void psychedelicModeChanged(bool);
void drawBackground();
void determineStep();
private:
QPixmap* bar() { return &m_barPixmap; }
QPixmap* bar() { return &barPixmap_; }
uint m_columns, m_rows; // number of rows and columns of blocks
uint m_y; // y-offset from top of widget
QPixmap m_barPixmap;
QPixmap m_topBarPixmap;
QPixmap m_background;
uint columns_, rows_; // number of rows and columns of blocks
uint y_; // y-offset from top of widget
QPixmap barPixmap_;
QPixmap topBarPixmap_;
QPixmap background_;
QPixmap canvas_;
Analyzer::Scope m_scope; // so we don't create a vector every frame
std::vector<float> m_store; // current bar heights
std::vector<float> m_yscale;
Analyzer::Scope scope_; // so we don't create a vector every frame
std::vector<float> store_; // current bar kHeights
std::vector<float> yscale_;
// FIXME why can't I namespace these? c++ issue?
std::vector<QPixmap> m_fade_bars;
std::vector<uint> m_fade_pos;
std::vector<int> m_fade_intensity;
std::vector<QPixmap> fade_bars_;
std::vector<uint> fade_pos_;
std::vector<int> fade_intensity_;
float m_step; // rows to fall per frame
float step_; // rows to fall per frame
};
#endif // ANALYZERS_BLOCKANALYZER_H_

View File

@ -2,7 +2,7 @@
Copyright 2004, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -28,69 +28,78 @@
using Analyzer::Scope;
const uint BoomAnalyzer::kColumnWidth = 4;
const uint BoomAnalyzer::kMaxBandCount = 256;
const uint BoomAnalyzer::kMinBandCount = 32;
const char* BoomAnalyzer::kName =
QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
K_barHeight(1.271) // 1.471
bands_(0),
scope_(kMinBandCount),
fg_(palette().color(QPalette::Highlight)),
K_barHeight_(1.271) // 1.471
,
F_peakSpeed(1.103) // 1.122
F_peakSpeed_(1.103) // 1.122
,
F(1.0),
bar_height(BAND_COUNT, 0),
peak_height(BAND_COUNT, 0),
peak_speed(BAND_COUNT, 0.01),
barPixmap(COLUMN_WIDTH, 50) {}
F_(1.0),
bar_height_(kMaxBandCount, 0),
peak_height_(kMaxBandCount, 0),
peak_speed_(kMaxBandCount, 0.01),
barPixmap_(kColumnWidth, 50) {
setMinimumWidth(kMinBandCount * (kColumnWidth + 1) - 1);
setMaximumWidth(kMaxBandCount * (kColumnWidth + 1) - 1);
}
void BoomAnalyzer::changeK_barHeight(int newValue) {
K_barHeight = static_cast<double>(newValue) / 1000;
K_barHeight_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
F_peakSpeed = static_cast<double>(newValue) / 1000;
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::resizeEvent(QResizeEvent*) { init(); }
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
void BoomAnalyzer::init() {
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
F = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
bands_ = qMin(
static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) +
1,
kMaxBandCount);
scope_.resize(bands_);
barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT);
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
QPainter p(&barPixmap);
QPainter p(&barPixmap_);
for (uint y = 0; y < HEIGHT; ++y) {
const double F = static_cast<double>(y) * h;
p.setPen(QColor(qMax(0, 255 - static_cast<int>(229.0 * F)),
qMax(0, 255 - static_cast<int>(229.0 * F)),
qMax(0, 255 - static_cast<int>(191.0 * F))));
p.drawLine(0, y, COLUMN_WIDTH - 2, y);
p.drawLine(0, y, kColumnWidth - 2, y);
}
updateBandSize(bands_);
}
void BoomAnalyzer::transform(Scope& s) {
float* front = static_cast<float*>(&s.front());
m_fht->spectrum(front);
m_fht->scale(front, 1.0 / 60);
fht_->spectrum(front);
fht_->scale(front, 1.0 / 50);
Scope scope(32, 0);
const uint xscale[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 19, 24, 29, 36,
43, 52, 63, 76, 91, 108, 129, 153, 182, 216, 255};
for (uint j, i = 0; i < 32; i++)
for (j = xscale[i]; j < xscale[i + 1]; j++)
if (s[j] > scope[i]) scope[i] = s[j];
s = scope;
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2
: scope_.size());
}
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
@ -104,47 +113,70 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Background));
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
h = log10(scope[i] * 256.0) * F;
Analyzer::interpolate(scope, scope_);
// update the graphics with the new colour
if (psychedelic_enabled_) {
paletteChange(QPalette());
}
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
h = log10(scope_[i] * 256.0) * F_;
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
if (h > bar_height[i]) {
bar_height[i] = h;
if (h > bar_height_[i]) {
bar_height_[i] = h;
if (h > peak_height[i]) {
peak_height[i] = h;
peak_speed[i] = 0.01;
if (h > peak_height_[i]) {
peak_height_[i] = h;
peak_speed_[i] = 0.01;
} else {
goto peak_handling;
}
} else {
if (bar_height[i] > 0.0) {
bar_height[i] -= K_barHeight; // 1.4
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
if (bar_height_[i] > 0.0) {
bar_height_[i] -= K_barHeight_; // 1.4
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
}
peak_handling:
if (peak_height[i] > 0.0) {
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; // 1.12
if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i];
peak_speed_[i] *= F_peakSpeed_; // 1.12
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
}
}
y = height() - uint(bar_height[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
canvas_painter.setPen(palette().color(QPalette::Highlight));
if (bar_height[i] > 0)
canvas_painter.drawRect(x, y, COLUMN_WIDTH - 1, height() - y - 1);
y = height() - uint(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0)
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
y = height() - uint(peak_height[i]);
canvas_painter.setPen(palette().color(QPalette::Base));
canvas_painter.drawLine(x, y, x + COLUMN_WIDTH - 1, y);
y = height() - uint(peak_height_[i]);
canvas_painter.setPen(palette().color(QPalette::Midlight));
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
}
p.drawPixmap(0, 0, canvas_);
}
void BoomAnalyzer::psychedelicModeChanged(bool enabled) {
psychedelic_enabled_ = enabled;
// reset colours back to normal
paletteChange(QPalette());
}
void BoomAnalyzer::paletteChange(const QPalette&) {
if (psychedelic_enabled_) {
fg_ = getPsychedelicColor(scope_, 50, 100);
} else {
// the highlight colour changes when the main window loses focus,
// so we use save and use the focused colour
fg_ = palette().color(QPalette::Highlight);
}
}

View File

@ -1,7 +1,7 @@
/* This file is part of Clementine.
Copyright 2004, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
@ -27,10 +27,6 @@
#include "analyzerbase.h"
/**
@author Max Howell
*/
class BoomAnalyzer : public Analyzer::Base {
Q_OBJECT
@ -39,9 +35,9 @@ class BoomAnalyzer : public Analyzer::Base {
static const char* kName;
virtual void init();
virtual void transform(Analyzer::Scope& s);
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
virtual void psychedelicModeChanged(bool);
public slots:
void changeK_barHeight(int);
@ -49,17 +45,23 @@ class BoomAnalyzer : public Analyzer::Base {
protected:
void resizeEvent(QResizeEvent* e);
void paletteChange(const QPalette&);
static const uint COLUMN_WIDTH = 4;
static const uint BAND_COUNT = 32;
static const uint kColumnWidth;
static const uint kMaxBandCount;
static const uint kMinBandCount;
double K_barHeight, F_peakSpeed, F;
uint bands_;
Analyzer::Scope scope_;
QColor fg_;
std::vector<float> bar_height;
std::vector<float> peak_height;
std::vector<float> peak_speed;
double K_barHeight_, F_peakSpeed_, F_;
QPixmap barPixmap;
std::vector<float> bar_height_;
std::vector<float> peak_height_;
std::vector<float> peak_speed_;
QPixmap barPixmap_;
QPixmap canvas_;
};

View File

@ -24,70 +24,69 @@
#include <string.h>
#include "fht.h"
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
FHT::FHT(int n) : buf_(0), tab_(0), log_(0) {
if (n < 3) {
m_num = 0;
m_exp2 = -1;
num_ = 0;
exp2_ = -1;
return;
}
m_exp2 = n;
m_num = 1 << n;
exp2_ = n;
num_ = 1 << n;
if (n > 3) {
m_buf = new float[m_num];
m_tab = new float[m_num * 2];
buf_ = new float[num_];
tab_ = new float[num_ * 2];
makeCasTable();
}
}
FHT::~FHT() {
delete[] m_buf;
delete[] m_tab;
delete[] m_log;
delete[] buf_;
delete[] tab_;
delete[] log_;
}
void FHT::makeCasTable(void) {
float d, *costab, *sintab;
int ul, ndiv2 = m_num / 2;
int ul, ndiv2 = num_ / 2;
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num;
ul++) {
for (costab = tab_, sintab = tab_ + num_ / 2 + 1, ul = 0; ul < num_; ul++) {
d = M_PI * ul / ndiv2;
*costab = *sintab = cos(d);
costab += 2, sintab += 2;
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
if (sintab > tab_ + num_ * 2) sintab = tab_ + 1;
}
}
float* FHT::copy(float* d, float* s) {
return static_cast<float*>(memcpy(d, s, m_num * sizeof(float)));
return static_cast<float*>(memcpy(d, s, num_ * sizeof(float)));
}
float* FHT::clear(float* d) {
return static_cast<float*>(memset(d, 0, m_num * sizeof(float)));
return static_cast<float*>(memset(d, 0, num_ * sizeof(float)));
}
void FHT::scale(float* p, float d) {
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
}
void FHT::ewma(float* d, float* s, float w) {
for (int i = 0; i < (m_num / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
}
void FHT::logSpectrum(float* out, float* p) {
int n = m_num / 2, i, j, k, *r;
if (!m_log) {
m_log = new int[n];
int n = num_ / 2, i, j, k, *r;
if (!log_) {
log_ = new int[n];
float f = n / log10(static_cast<double>(n));
for (i = 0, r = m_log; i < n; i++, r++) {
for (i = 0, r = log_; i < n; i++, r++) {
j = static_cast<int>(rint(log10(i + 1.0) * f));
*r = j >= n ? n - 1 : j;
}
}
semiLogSpectrum(p);
*out++ = *p = *p / 100;
for (k = i = 1, r = m_log; i < n; i++) {
for (k = i = 1, r = log_; i < n; i++) {
j = *r++;
if (i == j) {
*out++ = p[i];
@ -102,7 +101,7 @@ void FHT::logSpectrum(float* out, float* p) {
void FHT::semiLogSpectrum(float* p) {
float e;
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) {
for (int i = 0; i < (num_ / 2); i++, p++) {
e = 10.0 * log10(sqrt(*p * .5));
*p = e < 0 ? 0 : e;
}
@ -110,31 +109,31 @@ void FHT::semiLogSpectrum(float* p) {
void FHT::spectrum(float* p) {
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++)
for (int i = 0; i < (num_ / 2); i++, p++)
*p = static_cast<float>(sqrt(*p * .5));
}
void FHT::power(float* p) {
power2(p);
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
for (int i = 0; i < (num_ / 2); i++) *p++ *= .5;
}
void FHT::power2(float* p) {
int i;
float* q;
_transform(p, m_num, 0);
_transform(p, num_, 0);
*p = (*p * *p), *p += *p, p++;
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
for (i = 1, q = p + num_ - 2; i < (num_ / 2); i++, --q)
*p = (*p * *p) + (*q * *q), p++;
}
void FHT::transform(float* p) {
if (m_num == 8)
if (num_ == 8)
transform8(p);
else
_transform(p, m_num, 0);
_transform(p, num_, 0);
}
void FHT::transform8(float* p) {
@ -173,19 +172,19 @@ void FHT::_transform(float* p, int n, int k) {
int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
for (i = 0, t1 = buf_, t2 = buf_ + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++;
memcpy(p + k, m_buf, sizeof(float) * n);
memcpy(p + k, buf_, sizeof(float) * n);
_transform(p, ndiv2, k);
_transform(p, ndiv2, k + ndiv2);
j = m_num / ndiv2 - 1;
t1 = m_buf;
j = num_ / ndiv2 - 1;
t1 = buf_;
t2 = t1 + ndiv2;
t3 = p + k + ndiv2;
ptab = m_tab;
ptab = tab_;
pp = p + k;
a = *ptab++ * *t3++;
@ -202,5 +201,5 @@ void FHT::_transform(float* p, int n, int k) {
*t1++ = *pp + a;
*t2++ = *pp++ - a;
}
memcpy(p + k, m_buf, sizeof(float) * n);
memcpy(p + k, buf_, sizeof(float) * n);
}

View File

@ -32,11 +32,11 @@
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
*/
class FHT {
int m_exp2;
int m_num;
float* m_buf;
float* m_tab;
int* m_log;
int exp2_;
int num_;
float* buf_;
float* tab_;
int* log_;
/**
* Create a table of "cas" (cosine and sine) values.
@ -59,8 +59,8 @@ class FHT {
explicit FHT(int);
~FHT();
inline int sizeExp() const { return m_exp2; }
inline int size() const { return m_num; }
inline int sizeExp() const { return exp2_; }
inline int size() const { return num_; }
float* copy(float*, float*);
float* clear(float*);
void scale(float*, float);

View File

@ -57,7 +57,7 @@ NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
}
}
void NyanCatAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
void NyanCatAnalyzer::transform(Scope& s) { fht_->spectrum(&s.front()); }
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
if (e->timerId() == timer_id_) {
@ -74,7 +74,8 @@ void NyanCatAnalyzer::resizeEvent(QResizeEvent* e) {
buffer_[1] = QPixmap();
available_rainbow_width_ = width() - kCatWidth + kRainbowOverlap;
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
px_per_frame_ =
static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
}
@ -112,11 +113,13 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
QPointF* dest = polyline;
float* source = history_;
const float top_of_cat = static_cast<float>(height()) / 2 - static_cast<float>(kCatHeight) / 2;
const float top_of_cat =
static_cast<float>(height()) / 2 - static_cast<float>(kCatHeight) / 2;
for (int band = 0; band < kRainbowBands; ++band) {
// Calculate the Y position of this band.
const float y =
static_cast<float>(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + top_of_cat;
static_cast<float>(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) +
top_of_cat;
// Add each point in the line.
for (int x = 0; x < kHistorySize; ++x) {

View File

@ -1,6 +1,6 @@
/* This file is part of Clementine.
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, David Sansome <me@davidsansome.com>
@ -57,7 +57,7 @@ RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
}
}
void RainbowDashAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
void RainbowDashAnalyzer::transform(Scope& s) { fht_->spectrum(&s.front()); }
void RainbowDashAnalyzer::timerEvent(QTimerEvent* e) {
if (e->timerId() == timer_id_) {
@ -74,7 +74,8 @@ void RainbowDashAnalyzer::resizeEvent(QResizeEvent* e) {
buffer_[1] = QPixmap();
available_rainbow_width_ = width() - kDashWidth + kRainbowOverlap;
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
px_per_frame_ =
static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
}
@ -111,12 +112,13 @@ void RainbowDashAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
QPointF* dest = polyline;
float* source = history_;
const float top_of_Dash = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight) / 2;
const float top_of_Dash = static_cast<float>(height()) / 2 -
static_cast<float>(kRainbowHeight) / 2;
for (int band = 0; band < kRainbowBands; ++band) {
// Calculate the Y position of this band.
const float y =
static_cast<float>(kRainbowHeight) / (kRainbowBands + 1) * (band + 0.5) +
top_of_Dash;
const float y = static_cast<float>(kRainbowHeight) / (kRainbowBands + 1) *
(band + 0.5) +
top_of_Dash;
// Add each point in the line.
for (int x = 0; x < kHistorySize; ++x) {

View File

@ -1,6 +1,6 @@
/* This file is part of Clementine.
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify

View File

@ -2,7 +2,7 @@
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Clementine is free software: you can redistribute it and/or modify
@ -31,7 +31,8 @@ using Analyzer::Scope;
const char* Sonogram::kName =
QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
Sonogram::Sonogram(QWidget* parent) : Analyzer::Base(parent, 9) {}
Sonogram::Sonogram(QWidget* parent)
: Analyzer::Base(parent, 9), scope_size_(128) {}
Sonogram::~Sonogram() {}
@ -45,6 +46,12 @@ void Sonogram::resizeEvent(QResizeEvent* e) {
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
updateBandSize(scope_size_);
}
void Sonogram::psychedelicModeChanged(bool enabled) {
psychedelic_enabled_ = enabled;
updateBandSize(scope_size_);
}
void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
@ -60,20 +67,45 @@ void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
canvas_painter.drawPixmap(0, 0, canvas_, 1, 0, x, -1);
Scope::const_iterator it = s.begin(), end = s.end();
for (int y = height() - 1; y;) {
if (it >= end || *it < .005)
c = palette().color(QPalette::Background);
else if (*it < .05)
c.setHsv(95, 255, 255 - static_cast<int>(*it * 4000.0));
else if (*it < 1.0)
c.setHsv(95 - static_cast<int>(*it * 90.0), 255, 255);
else
c = Qt::red;
if (scope_size_ != s.size()) {
scope_size_ = s.size();
updateBandSize(scope_size_);
}
canvas_painter.setPen(c);
canvas_painter.drawPoint(x, y--);
if (psychedelic_enabled_) {
c = getPsychedelicColor(s, 20, 100);
for (int y = height() - 1; y;) {
if (it >= end || *it < .005) {
c = palette().color(QPalette::Background);
} else if (*it < .05) {
c.setHsv(c.hue(), c.saturation(), 255 - static_cast<int>(*it * 4000.0));
} else if (*it < 1.0) {
c.setHsv((c.hue() + static_cast<int>(*it * 90.0)) % 255, 255, 255);
} else {
c = getPsychedelicColor(s, 10, 50);
}
if (it < end) ++it;
canvas_painter.setPen(c);
canvas_painter.drawPoint(x, y--);
if (it < end) ++it;
}
} else {
for (int y = height() - 1; y;) {
if (it >= end || *it < .005)
c = palette().color(QPalette::Background);
else if (*it < .05)
c.setHsv(95, 255, 255 - static_cast<int>(*it * 4000.0));
else if (*it < 1.0)
c.setHsv(95 - static_cast<int>(*it * 90.0), 255, 255);
else
c = Qt::red;
canvas_painter.setPen(c);
canvas_painter.drawPoint(x, y--);
if (it < end) ++it;
}
}
canvas_painter.end();
@ -83,11 +115,11 @@ void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
void Sonogram::transform(Scope& scope) {
float* front = static_cast<float*>(&scope.front());
m_fht->power2(front);
m_fht->scale(front, 1.0 / 256);
scope.resize(m_fht->size() / 2);
fht_->power2(front);
fht_->scale(front, 1.0 / 256);
scope.resize(fht_->size() / 2);
}
void Sonogram::demo(QPainter& p) {
analyze(p, Scope(m_fht->size(), 0), new_frame_);
analyze(p, Scope(fht_->size(), 0), new_frame_);
}

View File

@ -3,6 +3,7 @@
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
Copyright 2015, Mark Furneaux <mark@furneaux.ca>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,10 +27,6 @@
#include "analyzerbase.h"
/**
@author Melchior FRANZ
*/
class Sonogram : public Analyzer::Base {
Q_OBJECT
public:
@ -43,8 +40,10 @@ class Sonogram : public Analyzer::Base {
void transform(Analyzer::Scope&);
void demo(QPainter& p);
void resizeEvent(QResizeEvent*);
void psychedelicModeChanged(bool);
QPixmap canvas_;
int scope_size_;
};
#endif // ANALYZERS_SONOGRAM_H_

View File

@ -2,7 +2,7 @@
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
Copyright 2003, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Mark Furneaux <mark@romaco.ca>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
@ -21,7 +21,7 @@
*/
/* Original Author: Stanislav Karchebny <berkus@users.sf.net> 2003
* Original Author: Max Howell <max.howell@methylblue.com> 2003
* Original Author: Max Howell <max.howell@methylblue.com> 2003
*/
#include <cmath>
@ -42,58 +42,64 @@ void TurbineAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
float h;
const uint hd2 = height() / 2;
const uint MAX_HEIGHT = hd2 - 1;
const uint kMaxHeight = hd2 - 1;
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Background));
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
h = log10(scope[i] * 256.0) * F * 0.5;
Analyzer::interpolate(scope, scope_);
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
// update the graphics with the new colour
if (psychedelic_enabled_) {
paletteChange(QPalette());
}
if (h > bar_height[i]) {
bar_height[i] = h;
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
h = log10(scope_[i] * 256.0) * F_ * 0.5;
if (h > peak_height[i]) {
peak_height[i] = h;
peak_speed[i] = 0.01;
if (h > kMaxHeight) h = kMaxHeight;
if (h > bar_height_[i]) {
bar_height_[i] = h;
if (h > peak_height_[i]) {
peak_height_[i] = h;
peak_speed_[i] = 0.01;
} else {
goto peak_handling;
}
} else {
if (bar_height[i] > 0.0) {
bar_height[i] -= K_barHeight; // 1.4
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
if (bar_height_[i] > 0.0) {
bar_height_[i] -= K_barHeight_; // 1.4
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
}
peak_handling:
if (peak_height[i] > 0.0) {
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; // 1.12
if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i];
peak_speed_[i] *= F_peakSpeed_; // 1.12
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
}
}
y = hd2 - static_cast<uint>(bar_height[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
canvas_painter.drawPixmap(x + 1, hd2, barPixmap, 0,
static_cast<int>(bar_height[i]),
-1, -1);
y = hd2 - static_cast<uint>(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.drawPixmap(x + 1, hd2, barPixmap_, 0,
static_cast<int>(bar_height_[i]), -1, -1);
canvas_painter.setPen(palette().color(QPalette::Highlight));
if (bar_height[i] > 0)
canvas_painter.drawRect(x, y, COLUMN_WIDTH - 1,
static_cast<int>(bar_height[i]) * 2 - 1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0)
canvas_painter.drawRect(x, y, kColumnWidth - 1,
static_cast<int>(bar_height_[i]) * 2 - 1);
const uint x2 = x + COLUMN_WIDTH - 1;
canvas_painter.setPen(palette().color(QPalette::Base));
y = hd2 - uint(peak_height[i]);
const uint x2 = x + kColumnWidth - 1;
canvas_painter.setPen(palette().color(QPalette::Midlight));
y = hd2 - uint(peak_height_[i]);
canvas_painter.drawLine(x, y, x2, y);
y = hd2 + uint(peak_height[i]);
y = hd2 + uint(peak_height_[i]);
canvas_painter.drawLine(x, y, x2, y);
}