Add boom and rainbow analyzers

This commit is contained in:
Jonas Kvinge 2019-02-04 21:34:12 +01:00
parent ad7084b897
commit 0679b78c1d
17 changed files with 973 additions and 296 deletions

View File

@ -29,6 +29,8 @@
<file>pictures/osd_shadow_corner.png</file> <file>pictures/osd_shadow_corner.png</file>
<file>pictures/osd_shadow_edge.png</file> <file>pictures/osd_shadow_edge.png</file>
<file>pictures/deezer.png</file> <file>pictures/deezer.png</file>
<file>fonts/HumongousofEternitySt.ttf</file> <file>pictures/nyancat.png</file>
<file>pictures/rainbowdash.png</file>
<file>fonts/HumongousofEternitySt.ttf</file>
</qresource> </qresource>
</RCC> </RCC>

BIN
data/pictures/nyancat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

34
dist/debian/copyright vendored
View File

@ -59,8 +59,8 @@ Files: src/core/main.h
src/scrobbler/* src/scrobbler/*
src/tidal/* src/tidal/*
src/deezer/* src/deezer/*
transcoderoptionswavpack.cpp src/transcoder/transcoderoptionswavpack.cpp
transcoderoptionswavpack.h src/transcoder/transcoderoptionswavpack.h
Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net> Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+ License: GPL-3+
@ -195,18 +195,46 @@ License: GPL-3+
Files: src/analyzer/fht.cpp Files: src/analyzer/fht.cpp
src/analyzer/fht.h src/analyzer/fht.h
Copyright: 2004, Melchior FRANZ - mfranz@kde.org Copyright: 2004, Melchior FRANZ <mfranz@kde.org>
2010, 2014, John Maguire <john.maguire@gmail.com>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
2017, Santiago Gil
License: GPL-2+ License: GPL-2+
Files: src/analyzer/analyzerbase.cpp Files: src/analyzer/analyzerbase.cpp
src/analyzer/analyzerbase.h src/analyzer/analyzerbase.h
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com> Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
2009-2012, David Sansome <me@davidsansome.com>
2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
2017, Santiago Gil
License: GPL-2+ License: GPL-2+
Files: src/analyzer/blockanalyzer.cpp Files: src/analyzer/blockanalyzer.cpp
src/analyzer/blockanalyzer.h src/analyzer/blockanalyzer.h
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com> Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
2005, Mark Kretschmann <markey@web.de> 2005, Mark Kretschmann <markey@web.de>
2009-2010, David Sansome <davidsansome@gmail.com>
2010, 2014, John Maguire <john.maguire@gmail.com>
License: GPL-2+
Files: src/analyzer/boomanalyzer.cpp
src/analyzer/boomanalyzer.h
Copyright: 2004, Max Howell <max.howell@methylblue.com>
2009-2010, David Sansome <davidsansome@gmail.com>
2010, 2014, John Maguire <john.maguire@gmail.com>
2014-2015, Mark Furneaux <mark@furneaux.ca>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
License: GPL-2+
Files: src/analyzer/rainbowanalyzer.cpp
src/analyzer/rainbowanalyzer.h
Copyright: 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
2011-2012, 2014, David Sansome <me@davidsansome.com>
2011, 2014, John Maguire <john.maguire@gmail.com>
2014, Alibek Omarov <a1ba.omarov@gmail.com>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
2014-2015, Mark Furneaux <mark@furneaux.ca>
2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
License: GPL-2+ License: GPL-2+
Files: src/collection/savedgroupingmanager.h Files: src/collection/savedgroupingmanager.h

View File

@ -125,6 +125,8 @@ set(SOURCES
analyzer/analyzerbase.cpp analyzer/analyzerbase.cpp
analyzer/analyzercontainer.cpp analyzer/analyzercontainer.cpp
analyzer/blockanalyzer.cpp analyzer/blockanalyzer.cpp
analyzer/boomanalyzer.cpp
analyzer/rainbowanalyzer.cpp
equalizer/equalizer.cpp equalizer/equalizer.cpp
equalizer/equalizerslider.cpp equalizer/equalizerslider.cpp
@ -307,6 +309,8 @@ set(HEADERS
analyzer/analyzerbase.h analyzer/analyzerbase.h
analyzer/analyzercontainer.h analyzer/analyzercontainer.h
analyzer/blockanalyzer.h analyzer/blockanalyzer.h
analyzer/boomanalyzer.h
analyzer/rainbowanalyzer.h
equalizer/equalizer.h equalizer/equalizer.h
equalizer/equalizerslider.h equalizer/equalizerslider.h

View File

@ -1,32 +1,40 @@
/*************************************************************************** /*
viswidget.cpp - description Strawberry Music Player
------------------- This file was part of Amarok.
begin : Die Jan 7 2003 Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
copyright : (C) 2003 by Max Howell Copyright 2009-2012, David Sansome <me@davidsansome.com>
email : markey@web.de Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
***************************************************************************/ Copyright 2017, Santiago Gil
/*************************************************************************** Strawberry is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* This program is free software; you can redistribute it and/or modify * the Free Software Foundation, either version 3 of the License, or
* it under the terms of the GNU General Public License as published by * (at your option) any later version.
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. * Strawberry is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
***************************************************************************/ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h" #include "config.h"
#include "analyzerbase.h"
#include <algorithm>
#include <cmath> #include <cmath>
#include <cstdint>
#include <QWidget> #include <QWidget>
#include <QVector>
#include <QPainter> #include <QPainter>
#include <QPalette> #include <QPalette>
#include <QTimerEvent> #include <QTimerEvent>
#include <QtEvents> #include <QtEvents>
#include "analyzerbase.h"
#include "core/logging.h" #include "core/logging.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
@ -36,10 +44,11 @@
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it // 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it
// 4. if you want to manipulate the scope, reimplement transform() // 4. if you want to manipulate the scope, reimplement transform()
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included // 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
// TODO make an INSTRUCTIONS file //
// TODO:
// Make an INSTRUCTIONS file
// can't mod scope in analyze you have to use transform // can't mod scope in analyze you have to use transform
// for 2D use setErasePixmap Qt function insetead of m_background
// TODO for 2D use setErasePixmap Qt function insetead of m_background
// make the linker happy only for gcc < 4.0 // make the linker happy only for gcc < 4.0
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \ #if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
@ -53,7 +62,6 @@ Analyzer::Base::Base(QWidget *parent, uint scopeSize)
fht_(new FHT(scopeSize)), fht_(new FHT(scopeSize)),
engine_(nullptr), engine_(nullptr),
lastscope_(512), lastscope_(512),
current_chunk_(0),
new_frame_(false), new_frame_(false),
is_playing_(false) {} is_playing_(false) {}
@ -63,19 +71,18 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) { void Analyzer::Base::transform(Scope& scope) {
// This is a standard transformation that should give an FFT scope that has bands for pretty analyzers QVector<float> aux(fht_->size());
if (aux.size() >= scope.size()) {
std::copy(scope.begin(), scope.end(), aux.begin());
}
else {
std::copy(scope.begin(), scope.begin() + aux.size(), aux.begin());
}
// NOTE: Resizing here is redundant as FHT routines only calculate FHT::size() values scope.resize( fht_->size() ); fht_->logSpectrum(scope.data(), aux.data());
fht_->scale(scope.data(), 1.0 / 20);
float *front = static_cast<float*>(&scope.front());
float *f = new float[fht_->size()];
fht_->copy(&f[0], front);
fht_->logSpectrum(front, &f[0]);
fht_->scale(front, 1.0 / 20);
scope.resize(fht_->size() / 2); // second half of values are rubbish scope.resize(fht_->size() / 2); // second half of values are rubbish
delete[] f;
} }

View File

@ -1,5 +1,24 @@
// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004 /*
// Copyright: See COPYING file that comes with this distribution Strawberry Music Player
This file was part of Amarok.
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
Copyright 2009-2012, David Sansome <me@davidsansome.com>
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2017, Santiago Gil
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANALYZERBASE_H #ifndef ANALYZERBASE_H
#define ANALYZERBASE_H #define ANALYZERBASE_H
@ -7,7 +26,7 @@
#include "config.h" #include "config.h"
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/types.h> # include <sys/types.h>
#endif #endif
#include <stdbool.h> #include <stdbool.h>
@ -23,6 +42,7 @@
#include "analyzer/fht.h" #include "analyzer/fht.h"
#include "engine/engine_fwd.h" #include "engine/engine_fwd.h"
#include "engine/enginebase.h"
class QHideEvent; class QHideEvent;
class QShowEvent; class QShowEvent;
@ -54,7 +74,7 @@ class Base : public QWidget {
virtual void framerateChanged() {} virtual void framerateChanged() {}
protected: protected:
Base(QWidget*, uint scopeSize = 7); explicit Base(QWidget*, uint scopeSize = 7);
void hideEvent(QHideEvent*); void hideEvent(QHideEvent*);
void showEvent(QShowEvent*); void showEvent(QShowEvent*);
@ -76,7 +96,7 @@ class Base : public QWidget {
FHT *fht_; FHT *fht_;
EngineBase *engine_; EngineBase *engine_;
Scope lastscope_; Scope lastscope_;
int current_chunk_;
bool new_frame_; bool new_frame_;
bool is_playing_; bool is_playing_;
}; };
@ -84,6 +104,7 @@ class Base : public QWidget {
void interpolate(const Scope&, Scope&); void interpolate(const Scope&, Scope&);
void initSin(Scope&, const uint = 6000); void initSin(Scope&, const uint = 6000);
} // END namespace Analyzer } // namespace Analyzer
#endif // ANALYZERBASE_H
#endif

View File

@ -38,6 +38,8 @@
#include "analyzerbase.h" #include "analyzerbase.h"
#include "blockanalyzer.h" #include "blockanalyzer.h"
#include "boomanalyzer.h"
#include "rainbowanalyzer.h"
#include "core/logging.h" #include "core/logging.h"
@ -64,6 +66,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
ignore_next_click_(false), ignore_next_click_(false),
current_analyzer_(nullptr), current_analyzer_(nullptr),
engine_(nullptr) { engine_(nullptr) {
QHBoxLayout *layout = new QHBoxLayout(this); QHBoxLayout *layout = new QHBoxLayout(this);
setLayout(layout); setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -79,6 +82,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
context_menu_->addSeparator(); context_menu_->addSeparator();
AddAnalyzerType<BlockAnalyzer>(); AddAnalyzerType<BlockAnalyzer>();
AddAnalyzerType<BoomAnalyzer>();
AddAnalyzerType<Rainbow::NyanCatAnalyzer>();
AddAnalyzerType<Rainbow::RainbowDashAnalyzer>();
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int))); connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer())); disable_action_ = context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
@ -93,6 +99,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
connect(double_click_timer_, SIGNAL(timeout()), SLOT(ShowPopupMenu())); connect(double_click_timer_, SIGNAL(timeout()), SLOT(ShowPopupMenu()));
Load(); Load();
} }
void AnalyzerContainer::SetActions(QAction *visualisation) { void AnalyzerContainer::SetActions(QAction *visualisation) {
@ -101,6 +108,7 @@ void AnalyzerContainer::SetActions(QAction *visualisation) {
} }
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) { void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
if (ignore_next_click_) { if (ignore_next_click_) {
ignore_next_click_ = false; ignore_next_click_ = false;
@ -114,6 +122,7 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
else if (e->button() == Qt::RightButton) { else if (e->button() == Qt::RightButton) {
context_menu_->popup(e->globalPos()); context_menu_->popup(e->globalPos());
} }
} }
void AnalyzerContainer::ShowPopupMenu() { void AnalyzerContainer::ShowPopupMenu() {
@ -144,6 +153,7 @@ void AnalyzerContainer::DisableAnalyzer() {
} }
void AnalyzerContainer::ChangeAnalyzer(int id) { void AnalyzerContainer::ChangeAnalyzer(int id) {
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this)); QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
if (!instance) { if (!instance) {
@ -161,9 +171,11 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
layout()->addWidget(current_analyzer_); layout()->addWidget(current_analyzer_);
Save(); Save();
} }
void AnalyzerContainer::ChangeFramerate(int new_framerate) { void AnalyzerContainer::ChangeFramerate(int new_framerate) {
if (current_analyzer_) { if (current_analyzer_) {
// Even if it is not supposed to happen, I don't want to get a dbz error // Even if it is not supposed to happen, I don't want to get a dbz error
new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate; new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate;
@ -173,14 +185,18 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
current_analyzer_->framerateChanged(); current_analyzer_->framerateChanged();
} }
SaveFramerate(new_framerate); SaveFramerate(new_framerate);
} }
void AnalyzerContainer::Load() { void AnalyzerContainer::Load() {
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
QString type = s.value("type", "BlockAnalyzer").toString();
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
s.endGroup();
// Analyzer // Analyzer
QString type = s.value("type", "BlockAnalyzer").toString();
if (type.isEmpty()) { if (type.isEmpty()) {
DisableAnalyzer(); DisableAnalyzer();
disable_action_->setChecked(true); disable_action_->setChecked(true);
@ -196,7 +212,6 @@ void AnalyzerContainer::Load() {
} }
// Framerate // Framerate
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
for (int i = 0; i < framerate_list_.count(); ++i) { for (int i = 0; i < framerate_list_.count(); ++i) {
if (current_framerate_ == framerate_list_[i]) { if (current_framerate_ == framerate_list_[i]) {
ChangeFramerate(current_framerate_); ChangeFramerate(current_framerate_);
@ -204,27 +219,35 @@ void AnalyzerContainer::Load() {
break; break;
} }
} }
} }
void AnalyzerContainer::SaveFramerate(int framerate) { void AnalyzerContainer::SaveFramerate(int framerate) {
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate? // For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
current_framerate_ = framerate; current_framerate_ = framerate;
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue(kSettingsFramerate, current_framerate_); s.setValue(kSettingsFramerate, current_framerate_);
s.endGroup();
} }
void AnalyzerContainer::Save() { void AnalyzerContainer::Save() {
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant()); s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
s.endGroup();
} }
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) { void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map())); QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
mapper_framerate_->setMapping(action, framerate); mapper_framerate_->setMapping(action, framerate);
group_framerate_->addAction(action); group_framerate_->addAction(action);
framerate_list_ << framerate; framerate_list_ << framerate;
action->setCheckable(true); action->setCheckable(true);
} }

View File

@ -26,12 +26,12 @@
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QList>
#include <QString>
#include <QPoint> #include <QPoint>
#include <QMenu> #include <QMenu>
#include <QAction> #include <QAction>
#include <QActionGroup> #include <QActionGroup>
#include <QList>
#include <QString>
#include <QSignalMapper> #include <QSignalMapper>
#include <QTimer> #include <QTimer>
#include <QtEvents> #include <QtEvents>
@ -120,5 +120,4 @@ void AnalyzerContainer::AddAnalyzerType() {
actions_ << action; actions_ << action;
} }
#endif #endif // ANALYZERCONTAINER_H

View File

@ -1,13 +1,30 @@
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003-5 /*
// Mark Kretschmann <markey@web.de>, (C) 2005 Strawberry Music Player
// Copyright: See COPYING file that comes with this distribution This file was part of Amarok.
// 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 2010, 2014, John Maguire <john.maguire@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "blockanalyzer.h" #include "blockanalyzer.h"
#include <cstdlib> #include <cstdlib>
#include <cmath> #include <cmath>
#include <scoped_allocator>
#include <QWidget> #include <QWidget>
#include <QPixmap> #include <QPixmap>
@ -19,12 +36,12 @@
#include "analyzerbase.h" #include "analyzerbase.h"
#include "fht.h" #include "fht.h"
const uint BlockAnalyzer::HEIGHT = 2; const uint BlockAnalyzer::kHeight = 2;
const uint BlockAnalyzer::WIDTH = 4; const uint BlockAnalyzer::kWidth = 4;
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary const uint BlockAnalyzer::kMinRows = 3; // arbituary
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary const uint BlockAnalyzer::kMinColumns = 32; // arbituary
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const uint BlockAnalyzer::FADE_SIZE = 90; const uint BlockAnalyzer::kFadeSize = 90;
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer"); const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
@ -34,18 +51,18 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
rows_(0), rows_(0),
y_(0), y_(0),
barpixmap_(1, 1), barpixmap_(1, 1),
topbarpixmap_(WIDTH, HEIGHT), topbarpixmap_(kWidth, kHeight),
scope_(MIN_COLUMNS), scope_(kMinColumns),
store_(1 << 8, 0), store_(1 << 8, 0),
fade_bars_(FADE_SIZE), fade_bars_(kFadeSize),
fade_pos_(1 << 8, 50), fade_pos_(1 << 8, 50),
fade_intensity_(1 << 8, 32) { fade_intensity_(1 << 8, 32) {
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there 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 // mxcl says null pixmaps cause crashes, so let's play it safe
for (uint i = 0; i < FADE_SIZE; ++i) fade_bars_[i] = QPixmap(1, 1); for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
} }
@ -61,20 +78,20 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
const uint oldRows = rows_; const uint oldRows = rows_;
// all is explained in analyze().. // all is explained in analyze()..
//+1 to counter -1 in maxSizes, trust me we need this! // +1 to counter -1 in maxSizes, trust me we need this!
columns_ = qMax(uint(double(width() + 1) / (WIDTH + 1)), MAX_COLUMNS); columns_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
rows_ = uint(double(height() + 1) / (HEIGHT + 1)); rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
// this is the y-offset for drawing from the top of the widget // this is the y-offset for drawing from the top of the widget
y_ = (height() - (rows_ * (HEIGHT + 1)) + 2) / 2; y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
scope_.resize(columns_); scope_.resize(columns_);
if (rows_ != oldRows) { if (rows_ != oldRows) {
barpixmap_ = QPixmap(WIDTH, rows_ * (HEIGHT + 1)); barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
for (uint i = 0; i < FADE_SIZE; ++i) for (uint i = 0; i < kFadeSize; ++i)
fade_bars_[i] = QPixmap(WIDTH, rows_ * (HEIGHT + 1)); fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
yscale_.resize(rows_ + 1); yscale_.resize(rows_ + 1);
@ -113,13 +130,11 @@ void BlockAnalyzer::transform(Analyzer::Scope &s) {
for (uint x = 0; x < s.size(); ++x) s[x] *= 2; for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
float* front = static_cast<float*>(&s.front()); fht_->spectrum(s.data());
fht_->scale(s.data(), 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 scope_.size() if large we prevent interpolation of large analyzers, this is good! // the second half is pretty dull, so only show it if the user has a large analyzer by setting to scope_.size() if large we prevent interpolation of large analyzers, this is good!
s.resize(scope_.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : scope_.size()); s.resize(scope_.size() <= kMaxColumns / 2 ? kMaxColumns / 2 : scope_.size());
} }
@ -154,35 +169,33 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
// determine y // determine y
for (y = 0; scope_[x] < 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) // This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
if ((float)y > store_[x]) if (static_cast<float>(y) > store_[x])
y = int(store_[x] += step_); y = static_cast<int>(store_[x] += step_);
else else
store_[x] = y; store_[x] = y;
// if y is lower than fade_pos_, then the bar has exceeded the height of the fadeout // If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
// if the fadeout is quite faded now, then display the new one // if the fadeout is quite faded now, then display the new one
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < FADE_SIZE / 3*/) { if (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
fade_pos_[x] = y; fade_pos_[x] = y;
fade_intensity_[x] = FADE_SIZE; fade_intensity_[x] = kFadeSize;
} }
if (fade_intensity_[x] > 0) { if (fade_intensity_[x] > 0) {
const uint offset = --fade_intensity_[x]; const uint offset = --fade_intensity_[x];
const uint y = y_ + (fade_pos_[x] * (HEIGHT + 1)); const uint y = y_ + (fade_pos_[x] * (kHeight + 1));
canvas_painter.drawPixmap(x * (WIDTH + 1), y, fade_bars_[offset], 0, 0, WIDTH, height() - y); canvas_painter.drawPixmap(x * (kWidth + 1), y, fade_bars_[offset], 0, 0, kWidth, height() - y);
} }
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_; if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are // REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, *bar(), canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
0, y * (HEIGHT + 1), bar()->width(),
bar()->height());
} }
for (uint x = 0; x < store_.size(); ++x) for (uint x = 0; x < store_.size(); ++x)
canvas_painter.drawPixmap(x * (WIDTH + 1), int(store_[x]) * (HEIGHT + 1) + y_, topbarpixmap_); canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
p.drawPixmap(0, 0, canvas_); p.drawPixmap(0, 0, canvas_);
@ -223,11 +236,11 @@ static inline void adjustToLimits(int &b, int &f, uint &amount) {
* It won't modify the hue of fg unless absolutely necessary * It won't modify the hue of fg unless absolutely necessary
* @return the adjusted form of fg * @return the adjusted form of fg
*/ */
QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) { QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount = 150) {
class OutputOnExit { class OutputOnExit {
public: public:
OutputOnExit(const QColor &color) : c(color) {} explicit OutputOnExit(const QColor &color) : c(color) {}
~OutputOnExit() { ~OutputOnExit() {
int h, s, v; int h, s, v;
c.getHsv(&h, &s, &v); c.getHsv(&h, &s, &v);
@ -237,14 +250,6 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
const QColor &c; const QColor &c;
}; };
// hack so I don't have to cast everywhere
#define amount static_cast<int>(_amount)
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP1( string ) debug() << string << ": " <<
// (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP2( string, value ) debug() << string << "=" << value << ":
// " << (QValueList<int>() << fh << fs << fv) << endl;
OutputOnExit allocateOnTheStack(fg); OutputOnExit allocateOnTheStack(fg);
int bh, bs, bv; int bh, bs, bv;
@ -255,23 +260,17 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
int dv = abs(bv - fv); int dv = abs(bv - fv);
// STAMP2( "DV", dv );
// value is the best measure of contrast // value is the best measure of contrast
// if there is enough difference in value already, return fg unchanged // if there is enough difference in value already, return fg unchanged
if (dv > amount) return fg; if (dv > static_cast<int>(amount)) return fg;
int ds = abs(bs - fs); int ds = abs(bs - fs);
// STAMP2( "DS", ds );
// saturation is good enough too. But not as good. TODO adapt this a little // saturation is good enough too. But not as good. TODO adapt this a little
if (ds > amount) return fg; if (ds > static_cast<int>(amount)) return fg;
int dh = abs(bh - fh); int dh = abs(bh - fh);
// STAMP2( "DH", dh );
if (dh > 120) { if (dh > 120) {
// a third of the colour wheel automatically guarentees contrast // a third of the colour wheel automatically guarentees contrast
// but only if the values are high enough and saturations significant enough // but only if the values are high enough and saturations significant enough
@ -279,79 +278,49 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
// check the saturation for the two colours is sufficient that hue alone can // check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast // provide sufficient contrast
if (ds > amount / 2 && (bs > 125 && fs > 125)) if (ds > static_cast<int>(amount) / 2 && (bs > 125 && fs > 125))
// STAMP1( "Sufficient saturation difference, and hues are
// compliemtary" );
return fg; return fg;
else if (dv > amount / 2 && (bv > 125 && fv > 125)) else if (dv > static_cast<int>(amount) / 2 && (bv > 125 && fv > 125))
// STAMP1( "Sufficient value difference, and hues are
// compliemtary" );
return fg; return fg;
// STAMP1( "Hues are complimentary but we must modify the value or
// saturation of the contrasting colour" );
// but either the colours are two desaturated, or too dark
// so we need to adjust the system, although not as much
///_amount /= 2;
} }
if (fs < 50 && ds < 40) { if (fs < 50 && ds < 40) {
// low saturation on a low saturation is sad // low saturation on a low saturation is sad
const int tmp = 50 - fs; const int tmp = 50 - fs;
fs = 50; fs = 50;
if (amount > tmp) if (static_cast<int>(amount) > tmp)
_amount -= tmp; amount -= tmp;
else else
_amount = 0; amount = 0;
} }
// test that there is available value to honor our contrast requirement // test that there is available value to honor our contrast requirement
if (255 - dv < amount) { if (255 - dv < static_cast<int>(amount)) {
// we have to modify the value and saturation of fg // we have to modify the value and saturation of fg
// adjustToLimits( bv, fv, amount ); // adjustToLimits( bv, fv, amount );
// STAMP
// see if we need to adjust the saturation // see if we need to adjust the saturation
if (amount > 0) adjustToLimits(bs, fs, _amount); if (static_cast<int>(amount) > 0) adjustToLimits(bs, fs, amount);
// STAMP
// see if we need to adjust the hue // see if we need to adjust the hue
if (amount > 0) fh += amount; // cycles around; if (static_cast<int>(amount) > 0)
fh += static_cast<int>(amount); // cycles around;
// STAMP
return QColor::fromHsv(fh, fs, fv); return QColor::fromHsv(fh, fs, fv);
} }
// STAMP if (fv > bv && bv > static_cast<int>(amount))
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount); if (fv < bv && fv > static_cast<int>(amount))
return QColor::fromHsv(fh, fs, fv - static_cast<int>(amount));
// STAMP if (fv > bv && (255 - fv > static_cast<int>(amount)))
return QColor::fromHsv(fh, fs, fv + static_cast<int>(amount));
if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - amount); if (fv < bv && (255 - bv > static_cast<int>(amount)))
return QColor::fromHsv(fh, fs, bv + static_cast<int>(amount));
// STAMP
if (fv > bv && (255 - fv > amount))
return QColor::fromHsv(fh, fs, fv + amount);
// STAMP
if (fv < bv && (255 - bv > amount))
return QColor::fromHsv(fh, fs, bv + amount);
// STAMP
// debug() << "Something went wrong!\n";
return Qt::blue; return Qt::blue;
#undef amount
// #undef STAMP
} }
void BlockAnalyzer::paletteChange(const QPalette&) { void BlockAnalyzer::paletteChange(const QPalette&) {
@ -361,17 +330,17 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
topbarpixmap_.fill(fg); topbarpixmap_.fill(fg);
const double dr = 15 * double(bg.red() - fg.red()) / (rows_ * 16); const double dr = 15 * static_cast<double>(bg.red() - fg.red()) / (rows_ * 16);
const double dg = 15 * double(bg.green() - fg.green()) / (rows_ * 16); const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
const double db = 15 * double(bg.blue() - fg.blue()) / (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(); const int r = fg.red(), g = fg.green(), b = fg.blue();
bar()->fill(bg); bar()->fill(bg);
QPainter p(bar()); QPainter p(bar());
for (int y = 0; (uint)y < rows_; ++y) for (int y = 0; static_cast<uint>(y) < rows_; ++y)
// graduate the fg color // graduate the fg color
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * y), g + int(dg * y), b + 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); const QColor bg = palette().color(QPalette::Background).dark(112);
@ -388,12 +357,12 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
const int r = bg.red(), g = bg.green(), b = bg.blue(); const int r = bg.red(), g = bg.green(), b = bg.blue();
// Precalculate all fade-bar pixmaps // Precalculate all fade-bar pixmaps
for (uint y = 0; y < FADE_SIZE; ++y) { for (uint y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Background)); fade_bars_[y].fill(palette().color(QPalette::Background));
QPainter f(&fade_bars_[y]); QPainter f(&fade_bars_[y]);
for (int z = 0; (uint)z < rows_; ++z) { for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE)); const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * Y), g + int(dg * Y), b + int(db * Y))); 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)));
} }
} }
} }
@ -404,14 +373,21 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
void BlockAnalyzer::drawBackground() { void BlockAnalyzer::drawBackground() {
if (background_.isNull()) {
return;
}
const QColor bg = palette().color(QPalette::Background); const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark(112); const QColor bgdark = bg.dark(112);
background_.fill(bg); background_.fill(bg);
QPainter p(&background_); QPainter p(&background_);
if (p.paintEngine() == 0) return;
for (int x = 0; (uint)x < columns_; ++x) for (int x = 0; (uint)x < columns_; ++x)
for (int y = 0; (uint)y < rows_; ++y) for (int y = 0; (uint)y < rows_; ++y)
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, WIDTH, HEIGHT, bgdark); p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
} }

View File

@ -1,6 +1,25 @@
// Maintainer: Max Howell <mac.howell@methylblue.com>, (C) 2003-5 /*
// Copyright: See COPYING file that comes with this distribution Strawberry Music Player
// This file was part of Amarok.
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 2010, 2014, John Maguire <john.maguire@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BLOCKANALYZER_H #ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H #define BLOCKANALYZER_H
@ -11,6 +30,7 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QVector>
#include <QString> #include <QString>
#include <QPixmap> #include <QPixmap>
#include <QPainter> #include <QPainter>
@ -21,22 +41,18 @@
class QResizeEvent; class QResizeEvent;
/**
* @author Max Howell
*/
class BlockAnalyzer : public Analyzer::Base { class BlockAnalyzer : public Analyzer::Base {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE BlockAnalyzer(QWidget*); Q_INVOKABLE BlockAnalyzer(QWidget*);
~BlockAnalyzer(); ~BlockAnalyzer();
static const uint HEIGHT; static const uint kHeight;
static const uint WIDTH; static const uint kWidth;
static const uint MIN_ROWS; static const uint kMinRows;
static const uint MIN_COLUMNS; static const uint kMinColumns;
static const uint MAX_COLUMNS; static const uint kMaxColumns;
static const uint FADE_SIZE; static const uint kFadeSize;
static const char *kName; static const char *kName;
@ -53,22 +69,21 @@ class BlockAnalyzer : public Analyzer::Base {
private: private:
QPixmap *bar() { return &barpixmap_; } QPixmap *bar() { return &barpixmap_; }
uint columns_, rows_; // number of rows and columns of blocks uint columns_, rows_; // number of rows and columns of blocks
uint y_; // y-offset from top of widget uint y_; // y-offset from top of widget
QPixmap barpixmap_; QPixmap barpixmap_;
QPixmap topbarpixmap_; QPixmap topbarpixmap_;
QPixmap background_; QPixmap background_;
QPixmap canvas_; QPixmap canvas_;
Analyzer::Scope scope_; // so we don't create a vector every frame Analyzer::Scope scope_; // so we don't create a vector every frame
std::vector<float> store_; // current bar heights QVector<float> store_; // current bar heights
std::vector<float> yscale_; QVector<float> yscale_;
// FIXME why can't I namespace these? c++ issue? QVector<QPixmap> fade_bars_;
std::vector<QPixmap> fade_bars_; QVector<uint> fade_pos_;
std::vector<uint> fade_pos_; QVector<int> fade_intensity_;
std::vector<int> fade_intensity_;
float step_; // rows to fall per frame float step_; // rows to fall per frame
}; };
#endif #endif // BLOCKANALYZER_H

View File

@ -0,0 +1,169 @@
/*
Strawberry Music Player
This file was part of Clementine.
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-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boomanalyzer.h"
#include <cmath>
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QColor>
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),
bands_(0),
scope_(kMinBandCount),
fg_(palette().color(QPalette::Highlight)),
K_barHeight_(1.271) // 1.471
,
F_peakSpeed_(1.103) // 1.122
,
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;
}
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
bands_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_);
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_);
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, kColumnWidth - 2, y);
}
}
void BoomAnalyzer::transform(Scope& s) {
fht_->spectrum(s.data());
fht_->scale(s.data(), 1.0 / 50);
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2 : scope_.size());
}
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
if (!new_frame || engine_->state() == Engine::Paused) {
p.drawPixmap(0, 0, canvas_);
return;
}
float h;
const uint MAX_HEIGHT = height() - 1;
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Background));
Analyzer::interpolate(scope, scope_);
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 > 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;
}
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] < 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(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::Midlight));
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
}
p.drawPixmap(0, 0, canvas_);
}

View File

@ -0,0 +1,75 @@
/*
Strawberry Music Player
This file was part of Clementine.
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-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BOOMANALYZER_H
#define BOOMANALYZER_H
#include "analyzerbase.h"
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QColor>
class QResizeEvent;
class BoomAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
Q_INVOKABLE BoomAnalyzer(QWidget*);
static const char* kName;
virtual void transform(Analyzer::Scope& s);
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
public slots:
void changeK_barHeight(int);
void changeF_peakSpeed(int);
protected:
void resizeEvent(QResizeEvent* e);
static const uint kColumnWidth;
static const uint kMaxBandCount;
static const uint kMinBandCount;
uint bands_;
Analyzer::Scope scope_;
QColor fg_;
double K_barHeight_, F_peakSpeed_, F_;
std::vector<float> bar_height_;
std::vector<float> peak_height_;
std::vector<float> peak_speed_;
QPixmap barPixmap_;
QPixmap canvas_;
};
#endif // BOOMANALYZER_H

View File

@ -1,140 +1,140 @@
// FHT - Fast Hartley Transform Class /*
// Strawberry Music Player
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org This file was part of Clementine.
// Copyright 2004, Melchior FRANZ <mfranz@kde.org>
// This program is free software; you can redistribute it and/or Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
// modify it under the terms of the GNU General Public License as Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
// published by the Free Software Foundation; either version 2 of the Copyright 2017, Santiago Gil
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA
//
// $Id$
#include <math.h> Strawberry is free software: you can redistribute it and/or modify
#include <string.h> it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fht.h" #include "fht.h"
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) { #include <algorithm>
if (n < 3) { #include <cmath>
m_num = 0;
m_exp2 = -1; #include <QVector>
return;
} FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
m_exp2 = n;
m_num = 1 << n;
if (n > 3) { if (n > 3) {
m_buf = new float[m_num]; buf_vector_.resize(num_);
m_tab = new float[m_num * 2]; tab_vector_.resize(num_ * 2);
makeCasTable(); makeCasTable();
} }
} }
FHT::~FHT() { FHT::~FHT() {}
delete[] m_buf;
delete[] m_tab; int FHT::sizeExp() const { return exp2_; }
delete[] m_log; int FHT::size() const { return num_; }
}
float* FHT::buf_() { return buf_vector_.data(); }
float* FHT::tab_() { return tab_vector_.data(); }
int* FHT::log_() { return log_vector_.data(); }
void FHT::makeCasTable(void) { void FHT::makeCasTable(void) {
float d, *costab, *sintab; float* costab = tab_();
int ul, ndiv2 = m_num / 2; float* sintab = tab_() + num_ / 2 + 1;
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) { for (int ul = 0; ul < num_; ul++) {
d = M_PI * ul / ndiv2; float d = M_PI * ul / (num_ / 2);
*costab = *sintab = cos(d); *costab = *sintab = cos(d);
costab += 2, sintab += 2; costab += 2;
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1; sintab += 2;
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1;
} }
} }
float* FHT::copy(float* d, float* s) {
return (float*)memcpy(d, s, m_num * sizeof(float));
}
float* FHT::clear(float* d) {
return (float*)memset(d, 0, m_num * sizeof(float));
}
void FHT::scale(float* p, float d) { 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) { 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) { void FHT::logSpectrum(float* out, float* p) {
int n = m_num / 2, i, j, k, *r;
if (!m_log) { int n = num_ / 2, i, j, k, *r;
m_log = new int[n]; if (log_vector_.size() < n) {
float f = n / log10((double)n); log_vector_.resize(n);
for (i = 0, r = m_log; i < n; i++, r++) { float f = n / log10(static_cast<double>(n));
j = int(rint(log10(i + 1.0) * f)); 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; *r = j >= n ? n - 1 : j;
} }
} }
semiLogSpectrum(p); semiLogSpectrum(p);
*out++ = *p = *p / 100; *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++; j = *r++;
if (i == j) if (i == j) {
*out++ = p[i]; *out++ = p[i];
}
else { else {
float base = p[k - 1]; float base = p[k - 1];
float step = (p[j] - base) / (j - (k - 1)); float step = (p[j] - base) / (j - (k - 1));
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr; for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
} }
} }
} }
void FHT::semiLogSpectrum(float* p) { void FHT::semiLogSpectrum(float* p) {
float e;
power2(p); 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)); float e = 10.0 * log10(sqrt(*p / 2));
*p = e < 0 ? 0 : e; *p = e < 0 ? 0 : e;
} }
} }
void FHT::spectrum(float* p) { void FHT::spectrum(float* p) {
power2(p); power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) *p = (float)sqrt(*p * .5); for (int i = 0; i < (num_ / 2); i++, p++)
*p = static_cast<float>(sqrt(*p / 2));
} }
void FHT::power(float* p) { void FHT::power(float* p) {
power2(p); power2(p);
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5; for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
} }
void FHT::power2(float* p) { void FHT::power2(float* p) {
int i; _transform(p, num_, 0);
float* q;
_transform(p, m_num, 0);
*p = (*p * *p), *p += *p, p++; *p = static_cast<float>(2 * pow(*p, 2));
p++;
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q) float* q = p + num_ - 2;
*p = (*p * *p) + (*q * *q), p++; for (int i = 1; i < (num_ / 2); i++) {
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
p++;
q--;
}
} }
void FHT::transform(float* p) { void FHT::transform(float* p) {
if (m_num == 8) if (num_ == 8)
transform8(p); transform8(p);
else else
_transform(p, m_num, 0); _transform(p, num_, 0);
} }
void FHT::transform8(float* p) { void FHT::transform8(float* p) {
float a, b, c, d, e, f, g, h, b_f2, d_h2; float a, b, c, d, e, f, g, h, b_f2, d_h2;
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh; float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
@ -159,6 +159,7 @@ void FHT::transform8(float* p) {
*--p = a_ce_g + b_df_h; *--p = a_ce_g + b_df_h;
*--p = ac_e_g + b_f2; *--p = ac_e_g + b_f2;
*--p = aceg + bdfh; *--p = aceg + bdfh;
} }
void FHT::_transform(float* p, int n, int k) { void FHT::_transform(float* p, int n, int k) {
@ -171,19 +172,19 @@ void FHT::_transform(float* p, int n, int k) {
int i, j, ndiv2 = n / 2; int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp; 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++; *t1++ = *pp++, *t2++ = *pp++;
memcpy(p + k, m_buf, sizeof(float) * n); std::copy(buf_(), buf_() + n, p + k);
_transform(p, ndiv2, k); _transform(p, ndiv2, k);
_transform(p, ndiv2, k + ndiv2); _transform(p, ndiv2, k + ndiv2);
j = m_num / ndiv2 - 1; j = num_ / ndiv2 - 1;
t1 = m_buf; t1 = buf_();
t2 = t1 + ndiv2; t2 = t1 + ndiv2;
t3 = p + k + ndiv2; t3 = p + k + ndiv2;
ptab = m_tab; ptab = tab_();
pp = p + k; pp = p + k;
a = *ptab++ * *t3++; a = *ptab++ * *t3++;
@ -200,5 +201,7 @@ void FHT::_transform(float* p, int n, int k) {
*t1++ = *pp + a; *t1++ = *pp + a;
*t2++ = *pp++ - a; *t2++ = *pp++ - a;
} }
memcpy(p + k, m_buf, sizeof(float) * n);
std::copy(buf_(), buf_() + n, p + k);
} }

View File

@ -1,26 +1,30 @@
// FHT - Fast Hartley Transform Class /*
// Strawberry Music Player
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org This file was part of Clementine.
// Copyright 2004, Melchior FRANZ <mfranz@kde.org>
// This program is free software; you can redistribute it and/or Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
// modify it under the terms of the GNU General Public License as Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
// published by the Free Software Foundation; either version 2 of the Copyright 2017, Santiago Gil
// License, or (at your option) any later version.
// Strawberry is free software: you can redistribute it and/or modify
// This program is distributed in the hope that it will be useful, but it under the terms of the GNU General Public License as published by
// WITHOUT ANY WARRANTY; without even the implied warranty of the Free Software Foundation, either version 3 of the License, or
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU (at your option) any later version.
// General Public License for more details.
// Strawberry is distributed in the hope that it will be useful,
// You should have received a copy of the GNU General Public License but WITHOUT ANY WARRANTY; without even the implied warranty of
// along with this program; if not, write to the Free Software MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA GNU General Public License for more details.
//
// $Id$ You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FHT_H #ifndef FHT_H
#define FHT_H #define FHT_H
#include <QVector>
/** /**
* Implementation of the Hartley Transform after Bracewell's discrete * Implementation of the Hartley Transform after Bracewell's discrete
* algorithm. The algorithm is subject to US patent No. 4,646,256 (1987) * algorithm. The algorithm is subject to US patent No. 4,646,256 (1987)
@ -30,11 +34,16 @@
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379 * [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
*/ */
class FHT { class FHT {
int m_exp2; const int num_;
int m_num; const int exp2_;
float* m_buf;
float* m_tab; QVector<float> buf_vector_;
int* m_log; QVector<float> tab_vector_;
QVector<int> log_vector_;
float* buf_();
float* tab_();
int* log_();
/** /**
* Create a table of "cas" (cosine and sine) values. * Create a table of "cas" (cosine and sine) values.
@ -57,10 +66,8 @@ class FHT {
FHT(int); FHT(int);
~FHT(); ~FHT();
inline int sizeExp() const { return m_exp2; } int sizeExp() const;
inline int size() const { return m_num; } int size() const;
float* copy(float*, float*);
float* clear(float*);
void scale(float*, float); void scale(float*, float);
/** /**
@ -115,4 +122,4 @@ class FHT {
void transform(float*); void transform(float*);
}; };
#endif #endif // FHT_H

View File

@ -0,0 +1,207 @@
/*
Strawberry Music Player
This file was part of Clementine.
Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
Copyright 2011, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rainbowanalyzer.h"
#include <cmath>
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QColor>
#include <QBrush>
#include <QPen>
#include <QTimerEvent>
#include "core/arraysize.h"
#include "core/logging.h"
using Analyzer::Scope;
const int Rainbow::RainbowAnalyzer::kHeight[] = {21, 33};
const int Rainbow::RainbowAnalyzer::kWidth[] = {34, 53};
const int Rainbow::RainbowAnalyzer::kFrameCount[] = {6, 16};
const int Rainbow::RainbowAnalyzer::kRainbowHeight[] = {21, 16};
const int Rainbow::RainbowAnalyzer::kRainbowOverlap[] = {13, 15};
const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33};
const char* Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
const char* Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02f;
Rainbow::RainbowAnalyzer::RainbowType Rainbow::RainbowAnalyzer::rainbowtype;
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent)
: Analyzer::Base(parent, 9),
timer_id_(startTimer(kFrameIntervalMs)),
frame_(0),
current_buffer_(0),
available_rainbow_width_(0),
px_per_frame_(0),
x_offset_(0),
background_brush_(QColor(0x0f, 0x43, 0x73))
{
rainbowtype = rbtype;
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
cat_dash_[1] = QPixmap(":/pictures/rainbowdash.png");
memset(history_, 0, sizeof(history_));
for (int i = 0; i < kRainbowBands; ++i) {
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
// pow constants computed so that
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
band_scale_[i] = -std::cos(M_PI * i / (kRainbowBands - 1)) * 0.5 * std::pow(2.3, i);
}
}
void Rainbow::RainbowAnalyzer::transform(Scope& s) { fht_->spectrum(s.data()); }
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
if (e->timerId() == timer_id_) {
frame_ = (frame_ + 1) % kFrameCount[rainbowtype];
}
else {
Analyzer::Base::timerEvent(e);
}
}
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
// Invalidate the buffer so it's recreated from scratch in the next paint event.
buffer_[0] = QPixmap();
buffer_[1] = QPixmap();
available_rainbow_width_ = width() - kWidth[rainbowtype] + kRainbowOverlap[rainbowtype];
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
}
void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
// Discard the second half of the transform
const int scope_size = s.size() / 2;
if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
// Transform the music into rainbows!
for (int band = 0; band < kRainbowBands; ++band) {
float* band_start = history_ + band * kHistorySize;
// Move the history of each band across by 1 frame.
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
}
// Now accumulate the scope data into each band. Should maybe use a series
// of band pass filters for this, so bands can leak into neighbouring bands,
// but for now it's a series of separate square filters.
const int samples_per_band = scope_size / kRainbowBands;
int sample = 0;
for (int band = 0; band < kRainbowBands; ++band) {
float accumulator = 0.0;
for (int i = 0; i < samples_per_band; ++i) {
accumulator += s[sample++];
}
history_[(band + 1) * kHistorySize - 1] = accumulator * band_scale_[band];
}
// Create polylines for the rainbows.
QPointF polyline[kRainbowBands * kHistorySize];
QPointF* dest = polyline;
float* source = history_;
const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2;
for (int band = 0; band < kRainbowBands; ++band) {
// Calculate the Y position of this band.
const float y = static_cast<float>(kRainbowHeight[rainbowtype]) / (kRainbowBands + 1) * (band + 0.5) + top_of;
// Add each point in the line.
for (int x = 0; x < kHistorySize; ++x) {
*dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
++dest;
++source;
}
}
// Do we have to draw the whole rainbow into the buffer?
if (buffer_[0].isNull()) {
for (int i = 0; i < 2; ++i) {
buffer_[i] = QPixmap(QSize(width() + x_offset_, height()));
buffer_[i].fill(background_brush_.color());
}
current_buffer_ = 0;
QPainter buffer_painter(&buffer_[0]);
buffer_painter.setRenderHint(QPainter::Antialiasing);
for (int band = kRainbowBands - 1; band >= 0; --band) {
buffer_painter.setPen(colors_[band]);
buffer_painter.drawPolyline(&polyline[band * kHistorySize], kHistorySize);
buffer_painter.drawPolyline(&polyline[band * kHistorySize], kHistorySize);
}
}
else {
const int last_buffer = current_buffer_;
current_buffer_ = (current_buffer_ + 1) % 2;
// We can just shuffle the buffer along a bit and draw the new frame's data.
QPainter buffer_painter(&buffer_[current_buffer_]);
buffer_painter.setRenderHint(QPainter::Antialiasing);
buffer_painter.drawPixmap(0, 0, buffer_[last_buffer], px_per_frame_, 0, x_offset_ + available_rainbow_width_ - px_per_frame_, 0);
buffer_painter.fillRect(x_offset_ + available_rainbow_width_ - px_per_frame_, 0, kWidth[rainbowtype] - kRainbowOverlap[rainbowtype] + px_per_frame_, height(), background_brush_);
for (int band = kRainbowBands - 1; band >= 0; --band) {
buffer_painter.setPen(colors_[band]);
buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3], 3);
}
}
}
// Draw the buffer on to the widget
p.drawPixmap(0, 0, buffer_[current_buffer_], x_offset_, 0, 0, 0);
// Draw rainbow analyzer (nyan cat or rainbowdash)
// Nyan nyan nyan nyan dash dash dash dash.
if (!is_playing_) {
// Ssshhh!
p.drawPixmap(SleepingDestRect(rainbowtype), cat_dash_[rainbowtype], SleepingSourceRect(rainbowtype));
}
else {
p.drawPixmap(DestRect(rainbowtype), cat_dash_[rainbowtype], SourceRect(rainbowtype));
}
}
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Nyancat, parent) {}
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {}

View File

@ -0,0 +1,141 @@
/*
Strawberry Music Player
This file was part of Clementine.
Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
Copyright 2011, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RAINBOWANALYZER_H
#define RAINBOWANALYZER_H
#include "analyzerbase.h"
#include <QObject>
#include <QWidget>
#include <QDateTime>
#include <QPainter>
#include <QPen>
namespace Rainbow {
class RainbowAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
enum RainbowType {
Nyancat = 0,
Dash = 1
};
RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent);
protected:
void transform(Analyzer::Scope&);
void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
void timerEvent(QTimerEvent* e);
void resizeEvent(QResizeEvent* e);
private:
static const int kHeight[];
static const int kWidth[];
static const int kFrameCount[];
static const int kRainbowHeight[];
static const int kRainbowOverlap[];
static const int kSleepingHeight[];
static const int kHistorySize = 128;
static const int kRainbowBands = 6;
static const float kPixelScale;
static const int kFrameIntervalMs = 150;
static RainbowType rainbowtype;
inline QRect SourceRect(RainbowType rainbowtype) const {
return QRect(0, kHeight[rainbowtype] * frame_, kWidth[rainbowtype], kHeight[rainbowtype]);
}
inline QRect SleepingSourceRect(RainbowType rainbowtype) const {
return QRect(0, kHeight[rainbowtype] * kFrameCount[rainbowtype], kWidth[rainbowtype], kSleepingHeight[rainbowtype]);
}
inline QRect DestRect(RainbowType rainbowtype) const {
return QRect(width() - kWidth[rainbowtype], (height() - kHeight[rainbowtype]) / 2, kWidth[rainbowtype], kHeight[rainbowtype]);
}
inline QRect SleepingDestRect(RainbowType rainbowtype) const {
return QRect(width() - kWidth[rainbowtype], (height() - kSleepingHeight[rainbowtype]) / 2, kWidth[rainbowtype], kSleepingHeight[rainbowtype]);
}
private:
// "constants" that get initialised in the constructor
float band_scale_[kRainbowBands];
QPen colors_[kRainbowBands];
// Rainbow Nyancat & Dash
QPixmap cat_dash_[2];
// For the cat or dash animation
int timer_id_;
int frame_;
// The y positions of each point on the rainbow.
float history_[kHistorySize * kRainbowBands];
// A cache of the last frame's rainbow,
// so it can be used in the next frame.
QPixmap buffer_[2];
int current_buffer_;
// Geometry information that's updated on resize:
// The width of the widget minus the space for the cat
int available_rainbow_width_;
// X spacing between each point in the polyline.
int px_per_frame_;
// Amount the buffer_ is shifted to the left (off the edge of the widget)
// to make the rainbow extend from 0 to available_rainbow_width_.
int x_offset_;
QBrush background_brush_;
};
class NyanCatAnalyzer : public RainbowAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE NyanCatAnalyzer(QWidget* parent);
static const char* kName;
};
class RainbowDashAnalyzer : public RainbowAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE RainbowDashAnalyzer(QWidget* parent);
static const char* kName;
};
}
#endif // RAINBOWANALYZER_H