From c102bf7fe6fb4fae7167b8c27c948cb4c8db6f8b Mon Sep 17 00:00:00 2001 From: Mark Furneaux Date: Wed, 1 Jul 2015 11:48:03 -0400 Subject: [PATCH] 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. --- src/analyzers/analyzerbase.cpp | 132 ++++++++++++---- src/analyzers/analyzerbase.h | 40 +++-- src/analyzers/analyzercontainer.cpp | 26 +++- src/analyzers/analyzercontainer.h | 7 +- src/analyzers/baranalyzer.cpp | 153 +++++++++++-------- src/analyzers/baranalyzer.h | 43 +++--- src/analyzers/blockanalyzer.cpp | 212 +++++++++++++++----------- src/analyzers/blockanalyzer.h | 41 ++--- src/analyzers/boomanalyzer.cpp | 136 ++++++++++------- src/analyzers/boomanalyzer.h | 28 ++-- src/analyzers/fht.cpp | 71 +++++---- src/analyzers/fht.h | 14 +- src/analyzers/nyancatanalyzer.cpp | 11 +- src/analyzers/rainbowdashanalyzer.cpp | 16 +- src/analyzers/rainbowdashanalyzer.h | 2 +- src/analyzers/sonogram.cpp | 68 ++++++--- src/analyzers/sonogram.h | 7 +- src/analyzers/turbine.cpp | 70 +++++---- 18 files changed, 660 insertions(+), 417 deletions(-) diff --git a/src/analyzers/analyzerbase.cpp b/src/analyzers/analyzerbase.cpp index 5346694a1..71c0b7ff0 100644 --- a/src/analyzers/analyzerbase.cpp +++ b/src/analyzers/analyzerbase.cpp @@ -2,7 +2,7 @@ Copyright 2003, Max Howell Copyright 2009, 2011-2012, David Sansome Copyright 2010, 2012, 2014, John Maguire - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify @@ -33,6 +33,7 @@ #include #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; #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()), + 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(&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(x) < m_fht->size(); ++x) { - m_lastScope[x] = - static_cast(thescope[i] + thescope[i + 1]) / (2 * (1 << 15)); + for (uint x = 0; static_cast(x) < fht_->size(); ++x) { + lastScope_[x] = static_cast(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(); diff --git a/src/analyzers/analyzerbase.h b/src/analyzers/analyzerbase.h index 503fc4604..73895caee 100644 --- a/src/analyzers/analyzerbase.h +++ b/src/analyzers/analyzerbase.h @@ -3,7 +3,7 @@ Copyright 2009-2010, David Sansome Copyright 2010, 2014, John Maguire Copyright 2011, Arnaud Bienner - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki 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 barkband_table_; + double prev_colors_[10][3]; + int prev_color_index_; + int bands_; + bool psychedelic_enabled_; }; void interpolate(const Scope&, Scope&); diff --git a/src/analyzers/analyzercontainer.cpp b/src/analyzers/analyzercontainer.cpp index 03037740a..676dd3cb7 100644 --- a/src/analyzers/analyzercontainer.cpp +++ b/src/analyzers/analyzercontainer.cpp @@ -3,7 +3,7 @@ Copyright 2010, 2014, John Maguire Copyright 2011-2012, Arnaud Bienner Copyright 2013, Vasily Fomin - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki 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())); diff --git a/src/analyzers/analyzercontainer.h b/src/analyzers/analyzercontainer.h index 46f0e942e..1a91c6d3d 100644 --- a/src/analyzers/analyzercontainer.h +++ b/src/analyzers/analyzercontainer.h @@ -4,6 +4,7 @@ Copyright 2011-2012, Arnaud Bienner Copyright 2013, Vasily Fomin Copyright 2014, Krzysztof Sobiecki + Copyright 2015, Mark Furneaux 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 void AddAnalyzerType(); void AddFramerate(const QString& name, int framerate); @@ -80,11 +83,13 @@ class AnalyzerContainer : public QWidget { QList framerate_list_; QList 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_; diff --git a/src/analyzers/baranalyzer.cpp b/src/analyzers/baranalyzer.cpp index effa439be..3992227c9 100644 --- a/src/analyzers/baranalyzer.cpp +++ b/src/analyzers/baranalyzer.cpp @@ -2,7 +2,7 @@ Copyright 2003, Mark Kretschmann Copyright 2009-2010, David Sansome Copyright 2014, Alibek Omarov - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Copyright 2014, John Maguire @@ -23,7 +23,6 @@ /* Original Author: Mark Kretschmann 2003 */ - #include "baranalyzer.h" #include #include @@ -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(m_bg.red() - fg.red()) / - (NUM_ROOFS - 1); // -1 because we start loop below at 0 - double dg = static_cast(m_bg.green() - fg.green()) / (NUM_ROOFS - 1); - double db = static_cast(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1); + double dr = static_cast(bg_.red() - fg.red()) / + (kNumRoofs - 1); // -1 because we start loop below at 0 + double dg = static_cast(bg_.green() - fg.green()) / (kNumRoofs - 1); + double db = static_cast(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(dr * i), fg.green() + static_cast(dg * i), - fg.blue() + static_cast(db * i))); + for (uint i = 0; i < kNumRoofs; ++i) { + pixRoof_[i] = QPixmap(kColumnWidth, 1); + pixRoof_[i].fill(QColor(fg.red() + static_cast(dr * i), + fg.green() + static_cast(dg * i), + fg.blue() + static_cast(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(height() - 2) / (log10(255) * MAX_AMPLITUDE); + const double F = + static_cast(height() - 2) / (log10(255) * MAX_AMPLITUDE); - BAND_COUNT = width() / 5; - MAX_DOWN = static_cast(0 - (qMax(1, height() / 50))); - MAX_UP = static_cast(qMax(1, height() / 25)); + band_count_ = width() / 5; + max_down_ = static_cast(0 - (qMax(1, height() / 50))); + max_up_ = static_cast(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(F * log10(x + 1)); + lvlMapper_[x] = static_cast(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(y) / height(); // p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 * // fraction) ) ); p.setPen(QColor(r + static_cast(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(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( + 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(y2) > roofVector[i]) { - roofVector[i] = static_cast(y2); - roofVelocityVector[i] = 1; + if (static_cast(y2) > roofVector_[i]) { + roofVector_[i] = static_cast(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]; } } } diff --git a/src/analyzers/baranalyzer.h b/src/analyzers/baranalyzer.h index c3e0411af..37159a47e 100644 --- a/src/analyzers/baranalyzer.h +++ b/src/analyzers/baranalyzer.h @@ -2,7 +2,7 @@ Copyright 2003-2005, Max Howell Copyright 2005, Mark Kretschmann Copyright 2009-2010, David Sansome - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof A. Sobiecki Copyright 2014, John Maguire @@ -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 m_roofMem[BAND_COUNT]; + QPixmap pixRoof_[kNumRoofs]; + // vector roofMem_[band_count_]; // Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope // every iteration - uint m_lvlMapper[256]; - std::vector m_roofMem; - std::vector barVector; // positions of bars - std::vector roofVector; // positions of roofs - std::vector roofVelocityVector; // speed that roofs falls + uint lvlMapper_[256]; + std::vector roofMem_; + std::vector barVector_; // positions of bars + std::vector roofVector_; // positions of roofs + std::vector 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_ diff --git a/src/analyzers/blockanalyzer.cpp b/src/analyzers/blockanalyzer.cpp index bb6807a63..e31e81d1a 100644 --- a/src/analyzers/blockanalyzer.cpp +++ b/src/analyzers/blockanalyzer.cpp @@ -3,7 +3,7 @@ Copyright 2005, Mark Kretschmann Copyright 2009-2010, David Sansome Copyright 2010, 2014, John Maguire - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify @@ -33,35 +33,34 @@ #include #include -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(static_cast(width() + 1) / (WIDTH + 1)) + 1, MAX_COLUMNS); - m_rows = static_cast(static_cast(height() + 1) / (HEIGHT + 1)); + columns_ = qMin( + static_cast(static_cast(width() + 1) / (kWidth + 1)) + 1, + kMaxColumns); + rows_ = static_cast(static_cast(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(m_rows * timeout()) / fallTime; + step_ = static_cast(rows_ * timeout()) / fallTime; } void BlockAnalyzer::framerateChanged() { // virtual @@ -127,15 +129,14 @@ void BlockAnalyzer::transform(Analyzer::Scope& s) { float* front = static_cast(&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(y) > m_store[x]) - y = static_cast(m_store[x] += m_step); + if (static_cast(y) > store_[x]) + y = static_cast(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(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(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(_amount) > 0) adjustToLimits(bs, fs, _amount); // see if we need to adjust the hue - if (static_cast(_amount) > 0) fh += static_cast(_amount); // cycles around; + if (static_cast(_amount) > 0) + fh += static_cast(_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(bg.red() - fg.red()) / (m_rows * 16); - const double dg = 15 * static_cast(bg.green() - fg.green()) / (m_rows * 16); - const double db = 15 * static_cast(bg.blue() - fg.blue()) / (m_rows * 16); + topBarPixmap_.fill(fg); + + const double dr = + 15 * static_cast(bg.red() - fg.red()) / (rows_ * 16); + const double dg = + 15 * static_cast(bg.green() - fg.green()) / (rows_ * 16); + const double db = + 15 * static_cast(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(y) < m_rows; ++y) + + for (int y = 0; static_cast(y) < rows_; ++y) // graduate the fg color - p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT, - QColor(r + static_cast(dr * y), g + static_cast(dg * y), - b + static_cast(db * y))); + p.fillRect( + 0, y * (kHeight + 1), kWidth, kHeight, + QColor(r + static_cast(dr * y), g + static_cast(dg * y), + b + static_cast(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(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(dr * Y), g + static_cast(dg * Y), b + static_cast(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(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(dr * Y), g + static_cast(dg * Y), + b + static_cast(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); } diff --git a/src/analyzers/blockanalyzer.h b/src/analyzers/blockanalyzer.h index b055cb86d..04a9dcb38 100644 --- a/src/analyzers/blockanalyzer.h +++ b/src/analyzers/blockanalyzer.h @@ -2,7 +2,7 @@ Copyright 2003-2005, Max Howell Copyright 2009-2010, David Sansome Copyright 2010, 2014, John Maguire - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof A. Sobiecki 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 m_store; // current bar heights - std::vector m_yscale; + Analyzer::Scope scope_; // so we don't create a vector every frame + std::vector store_; // current bar kHeights + std::vector yscale_; // FIXME why can't I namespace these? c++ issue? - std::vector m_fade_bars; - std::vector m_fade_pos; - std::vector m_fade_intensity; + std::vector fade_bars_; + std::vector fade_pos_; + std::vector fade_intensity_; - float m_step; // rows to fall per frame + float step_; // rows to fall per frame }; #endif // ANALYZERS_BLOCKANALYZER_H_ diff --git a/src/analyzers/boomanalyzer.cpp b/src/analyzers/boomanalyzer.cpp index 94c8dfc86..93df2e48a 100644 --- a/src/analyzers/boomanalyzer.cpp +++ b/src/analyzers/boomanalyzer.cpp @@ -2,7 +2,7 @@ Copyright 2004, Max Howell Copyright 2009-2010, David Sansome Copyright 2010, 2014, John Maguire - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki 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(newValue) / 1000; + K_barHeight_ = static_cast(newValue) / 1000; } void BoomAnalyzer::changeF_peakSpeed(int newValue) { - F_peakSpeed = static_cast(newValue) / 1000; + F_peakSpeed_ = static_cast(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(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/); + bands_ = qMin( + static_cast(static_cast(width() + 1) / (kColumnWidth + 1)) + + 1, + kMaxBandCount); + scope_.resize(bands_); - barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT); + F_ = static_cast(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(y) * h; p.setPen(QColor(qMax(0, 255 - static_cast(229.0 * F)), qMax(0, 255 - static_cast(229.0 * F)), qMax(0, 255 - static_cast(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(&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); + } +} diff --git a/src/analyzers/boomanalyzer.h b/src/analyzers/boomanalyzer.h index 5f087c339..d25a3abae 100644 --- a/src/analyzers/boomanalyzer.h +++ b/src/analyzers/boomanalyzer.h @@ -1,7 +1,7 @@ /* This file is part of Clementine. Copyright 2004, Max Howell Copyright 2009-2010, David Sansome - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Copyright 2014, John Maguire @@ -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 bar_height; - std::vector peak_height; - std::vector peak_speed; + double K_barHeight_, F_peakSpeed_, F_; - QPixmap barPixmap; + std::vector bar_height_; + std::vector peak_height_; + std::vector peak_speed_; + + QPixmap barPixmap_; QPixmap canvas_; }; diff --git a/src/analyzers/fht.cpp b/src/analyzers/fht.cpp index 5ad262ad5..38e9b7c5a 100644 --- a/src/analyzers/fht.cpp +++ b/src/analyzers/fht.cpp @@ -24,70 +24,69 @@ #include #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(memcpy(d, s, m_num * sizeof(float))); + return static_cast(memcpy(d, s, num_ * sizeof(float))); } float* FHT::clear(float* d) { - return static_cast(memset(d, 0, m_num * sizeof(float))); + return static_cast(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(n)); - for (i = 0, r = m_log; i < n; i++, r++) { + for (i = 0, r = log_; i < n; i++, r++) { j = static_cast(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(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); } diff --git a/src/analyzers/fht.h b/src/analyzers/fht.h index 77703b04f..6e37fee2c 100644 --- a/src/analyzers/fht.h +++ b/src/analyzers/fht.h @@ -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); diff --git a/src/analyzers/nyancatanalyzer.cpp b/src/analyzers/nyancatanalyzer.cpp index 890d55f44..2424f5635 100644 --- a/src/analyzers/nyancatanalyzer.cpp +++ b/src/analyzers/nyancatanalyzer.cpp @@ -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(available_rainbow_width_) / (kHistorySize - 1) + 1; + px_per_frame_ = + static_cast(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(height()) / 2 - static_cast(kCatHeight) / 2; + const float top_of_cat = + static_cast(height()) / 2 - static_cast(kCatHeight) / 2; for (int band = 0; band < kRainbowBands; ++band) { // Calculate the Y position of this band. const float y = - static_cast(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + top_of_cat; + static_cast(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + + top_of_cat; // Add each point in the line. for (int x = 0; x < kHistorySize; ++x) { diff --git a/src/analyzers/rainbowdashanalyzer.cpp b/src/analyzers/rainbowdashanalyzer.cpp index 29c968cc4..c7182163d 100644 --- a/src/analyzers/rainbowdashanalyzer.cpp +++ b/src/analyzers/rainbowdashanalyzer.cpp @@ -1,6 +1,6 @@ /* This file is part of Clementine. Copyright 2014, Alibek Omarov - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Copyright 2014, David Sansome @@ -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(available_rainbow_width_) / (kHistorySize - 1) + 1; + px_per_frame_ = + static_cast(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(height()) / 2 - static_cast(kRainbowHeight) / 2; + const float top_of_Dash = static_cast(height()) / 2 - + static_cast(kRainbowHeight) / 2; for (int band = 0; band < kRainbowBands; ++band) { // Calculate the Y position of this band. - const float y = - static_cast(kRainbowHeight) / (kRainbowBands + 1) * (band + 0.5) + - top_of_Dash; + const float y = static_cast(kRainbowHeight) / (kRainbowBands + 1) * + (band + 0.5) + + top_of_Dash; // Add each point in the line. for (int x = 0; x < kHistorySize; ++x) { diff --git a/src/analyzers/rainbowdashanalyzer.h b/src/analyzers/rainbowdashanalyzer.h index b5a73b8cf..d8d97c11c 100644 --- a/src/analyzers/rainbowdashanalyzer.h +++ b/src/analyzers/rainbowdashanalyzer.h @@ -1,6 +1,6 @@ /* This file is part of Clementine. Copyright 2014, Alibek Omarov - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify diff --git a/src/analyzers/sonogram.cpp b/src/analyzers/sonogram.cpp index 879414586..ae2068069 100644 --- a/src/analyzers/sonogram.cpp +++ b/src/analyzers/sonogram.cpp @@ -2,7 +2,7 @@ Copyright 2004, Melchior FRANZ Copyright 2009-2010, David Sansome Copyright 2010, 2014, John Maguire - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki 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(*it * 4000.0)); - else if (*it < 1.0) - c.setHsv(95 - static_cast(*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(*it * 4000.0)); + } else if (*it < 1.0) { + c.setHsv((c.hue() + static_cast(*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(*it * 4000.0)); + else if (*it < 1.0) + c.setHsv(95 - static_cast(*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(&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_); } diff --git a/src/analyzers/sonogram.h b/src/analyzers/sonogram.h index 73fe9307d..7588c5264 100644 --- a/src/analyzers/sonogram.h +++ b/src/analyzers/sonogram.h @@ -3,6 +3,7 @@ Copyright 2009-2010, David Sansome Copyright 2014, Krzysztof Sobiecki Copyright 2014, John Maguire + Copyright 2015, Mark Furneaux 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_ diff --git a/src/analyzers/turbine.cpp b/src/analyzers/turbine.cpp index 7d409a5d9..0572bc270 100644 --- a/src/analyzers/turbine.cpp +++ b/src/analyzers/turbine.cpp @@ -2,7 +2,7 @@ Copyright 2003, Stanislav Karchebny Copyright 2003, Max Howell Copyright 2009-2010, David Sansome - Copyright 2014, Mark Furneaux + Copyright 2014-2015, Mark Furneaux Copyright 2014, Krzysztof Sobiecki Copyright 2014, John Maguire @@ -21,7 +21,7 @@ */ /* Original Author: Stanislav Karchebny 2003 - * Original Author: Max Howell 2003 + * Original Author: Max Howell 2003 */ #include @@ -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(bar_height[i]); - canvas_painter.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1); - canvas_painter.drawPixmap(x + 1, hd2, barPixmap, 0, - static_cast(bar_height[i]), - -1, -1); + y = hd2 - static_cast(bar_height_[i]); + canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1); + canvas_painter.drawPixmap(x + 1, hd2, barPixmap_, 0, + static_cast(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(bar_height[i]) * 2 - 1); + canvas_painter.setPen(fg_); + if (bar_height_[i] > 0) + canvas_painter.drawRect(x, y, kColumnWidth - 1, + static_cast(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); }