diff --git a/data/data.qrc b/data/data.qrc
index 5af418be..88f96384 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -29,6 +29,8 @@
pictures/osd_shadow_corner.png
pictures/osd_shadow_edge.png
pictures/deezer.png
- fonts/HumongousofEternitySt.ttf
+ pictures/nyancat.png
+ pictures/rainbowdash.png
+ fonts/HumongousofEternitySt.ttf
diff --git a/data/pictures/nyancat.png b/data/pictures/nyancat.png
new file mode 100644
index 00000000..7ce86474
Binary files /dev/null and b/data/pictures/nyancat.png differ
diff --git a/data/pictures/rainbowdash.png b/data/pictures/rainbowdash.png
new file mode 100644
index 00000000..db52cb5b
Binary files /dev/null and b/data/pictures/rainbowdash.png differ
diff --git a/dist/debian/copyright b/dist/debian/copyright
index 547e559e..561d8e8a 100644
--- a/dist/debian/copyright
+++ b/dist/debian/copyright
@@ -59,8 +59,8 @@ Files: src/core/main.h
src/scrobbler/*
src/tidal/*
src/deezer/*
- transcoderoptionswavpack.cpp
- transcoderoptionswavpack.h
+ src/transcoder/transcoderoptionswavpack.cpp
+ src/transcoder/transcoderoptionswavpack.h
Copyright: 2012-2014, 2017-2018, Jonas Kvinge
License: GPL-3+
@@ -195,18 +195,46 @@ License: GPL-3+
Files: src/analyzer/fht.cpp
src/analyzer/fht.h
-Copyright: 2004, Melchior FRANZ - mfranz@kde.org
+Copyright: 2004, Melchior FRANZ
+ 2010, 2014, John Maguire
+ 2014, Krzysztof Sobiecki
+ 2017, Santiago Gil
License: GPL-2+
Files: src/analyzer/analyzerbase.cpp
src/analyzer/analyzerbase.h
Copyright: 2003-2005, Max Howell
+ 2009-2012, David Sansome
+ 2010, 2012, 2014, John Maguire
+ 2017, Santiago Gil
License: GPL-2+
Files: src/analyzer/blockanalyzer.cpp
src/analyzer/blockanalyzer.h
Copyright: 2003-2005, Max Howell
2005, Mark Kretschmann
+ 2009-2010, David Sansome
+ 2010, 2014, John Maguire
+License: GPL-2+
+
+Files: src/analyzer/boomanalyzer.cpp
+ src/analyzer/boomanalyzer.h
+Copyright: 2004, Max Howell
+ 2009-2010, David Sansome
+ 2010, 2014, John Maguire
+ 2014-2015, Mark Furneaux
+ 2014, Krzysztof Sobiecki
+License: GPL-2+
+
+Files: src/analyzer/rainbowanalyzer.cpp
+ src/analyzer/rainbowanalyzer.h
+Copyright: 2011, Tyler Rhodes
+ 2011-2012, 2014, David Sansome
+ 2011, 2014, John Maguire
+ 2014, Alibek Omarov
+ 2014, Krzysztof Sobiecki
+ 2014-2015, Mark Furneaux
+ 2015, Arun Narayanankutty
License: GPL-2+
Files: src/collection/savedgroupingmanager.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d684a612..cab682bb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -125,6 +125,8 @@ set(SOURCES
analyzer/analyzerbase.cpp
analyzer/analyzercontainer.cpp
analyzer/blockanalyzer.cpp
+ analyzer/boomanalyzer.cpp
+ analyzer/rainbowanalyzer.cpp
equalizer/equalizer.cpp
equalizer/equalizerslider.cpp
@@ -307,6 +309,8 @@ set(HEADERS
analyzer/analyzerbase.h
analyzer/analyzercontainer.h
analyzer/blockanalyzer.h
+ analyzer/boomanalyzer.h
+ analyzer/rainbowanalyzer.h
equalizer/equalizer.h
equalizer/equalizerslider.h
diff --git a/src/analyzer/analyzerbase.cpp b/src/analyzer/analyzerbase.cpp
index 5536e965..37208655 100644
--- a/src/analyzer/analyzerbase.cpp
+++ b/src/analyzer/analyzerbase.cpp
@@ -1,32 +1,40 @@
-/***************************************************************************
- viswidget.cpp - description
- -------------------
- begin : Die Jan 7 2003
- copyright : (C) 2003 by Max Howell
- email : markey@web.de
- ***************************************************************************/
+/*
+ Strawberry Music Player
+ This file was part of Amarok.
+ Copyright 2003-2004, Max Howell
+ Copyright 2009-2012, David Sansome
+ Copyright 2010, 2012, 2014, John Maguire
+ Copyright 2017, Santiago Gil
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
+ 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 .
+*/
#include "config.h"
+#include "analyzerbase.h"
+
+#include
#include
+#include
#include
+#include
#include
#include
#include
#include
-#include "analyzerbase.h"
-
#include "core/logging.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
// 4. if you want to manipulate the scope, reimplement transform()
// 5. for convenience are pre-included
-// TODO make an INSTRUCTIONS file
+//
+// TODO:
+// Make an INSTRUCTIONS file
// can't mod scope in analyze you have to use transform
-
-// TODO for 2D use setErasePixmap Qt function insetead of m_background
+// 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,7 +62,6 @@ Analyzer::Base::Base(QWidget *parent, uint scopeSize)
fht_(new FHT(scopeSize)),
engine_(nullptr),
lastscope_(512),
- current_chunk_(0),
new_frame_(false),
is_playing_(false) {}
@@ -63,19 +71,18 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) {
- // This is a standard transformation that should give an FFT scope that has bands for pretty analyzers
+ QVector 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() );
-
- float *front = static_cast(&scope.front());
-
- float *f = new float[fht_->size()];
- fht_->copy(&f[0], front);
- fht_->logSpectrum(front, &f[0]);
- fht_->scale(front, 1.0 / 20);
+ fht_->logSpectrum(scope.data(), aux.data());
+ fht_->scale(scope.data(), 1.0 / 20);
scope.resize(fht_->size() / 2); // second half of values are rubbish
- delete[] f;
}
diff --git a/src/analyzer/analyzerbase.h b/src/analyzer/analyzerbase.h
index 2ec8354d..910dff5c 100644
--- a/src/analyzer/analyzerbase.h
+++ b/src/analyzer/analyzerbase.h
@@ -1,5 +1,24 @@
-// Maintainer: Max Howell , (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
+ Copyright 2009-2012, David Sansome
+ Copyright 2010, 2012, 2014, John Maguire
+ 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 .
+*/
#ifndef ANALYZERBASE_H
#define ANALYZERBASE_H
@@ -7,7 +26,7 @@
#include "config.h"
#ifdef __FreeBSD__
-#include
+# include
#endif
#include
@@ -23,6 +42,7 @@
#include "analyzer/fht.h"
#include "engine/engine_fwd.h"
+#include "engine/enginebase.h"
class QHideEvent;
class QShowEvent;
@@ -54,7 +74,7 @@ class Base : public QWidget {
virtual void framerateChanged() {}
protected:
- Base(QWidget*, uint scopeSize = 7);
+ explicit Base(QWidget*, uint scopeSize = 7);
void hideEvent(QHideEvent*);
void showEvent(QShowEvent*);
@@ -76,7 +96,7 @@ class Base : public QWidget {
FHT *fht_;
EngineBase *engine_;
Scope lastscope_;
- int current_chunk_;
+
bool new_frame_;
bool is_playing_;
};
@@ -84,6 +104,7 @@ class Base : public QWidget {
void interpolate(const Scope&, Scope&);
void initSin(Scope&, const uint = 6000);
-} // END namespace Analyzer
+} // namespace Analyzer
+
+#endif // ANALYZERBASE_H
-#endif
diff --git a/src/analyzer/analyzercontainer.cpp b/src/analyzer/analyzercontainer.cpp
index 6397f76a..5beb2ded 100644
--- a/src/analyzer/analyzercontainer.cpp
+++ b/src/analyzer/analyzercontainer.cpp
@@ -38,6 +38,8 @@
#include "analyzerbase.h"
#include "blockanalyzer.h"
+#include "boomanalyzer.h"
+#include "rainbowanalyzer.h"
#include "core/logging.h"
@@ -64,6 +66,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
ignore_next_click_(false),
current_analyzer_(nullptr),
engine_(nullptr) {
+
QHBoxLayout *layout = new QHBoxLayout(this);
setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0);
@@ -79,6 +82,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
context_menu_->addSeparator();
AddAnalyzerType();
+ AddAnalyzerType();
+ AddAnalyzerType();
+ AddAnalyzerType();
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
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()));
Load();
+
}
void AnalyzerContainer::SetActions(QAction *visualisation) {
@@ -101,6 +108,7 @@ void AnalyzerContainer::SetActions(QAction *visualisation) {
}
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
+
if (e->button() == Qt::LeftButton) {
if (ignore_next_click_) {
ignore_next_click_ = false;
@@ -114,6 +122,7 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
else if (e->button() == Qt::RightButton) {
context_menu_->popup(e->globalPos());
}
+
}
void AnalyzerContainer::ShowPopupMenu() {
@@ -144,6 +153,7 @@ void AnalyzerContainer::DisableAnalyzer() {
}
void AnalyzerContainer::ChangeAnalyzer(int id) {
+
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
if (!instance) {
@@ -161,9 +171,11 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
layout()->addWidget(current_analyzer_);
Save();
+
}
void AnalyzerContainer::ChangeFramerate(int new_framerate) {
+
if (current_analyzer_) {
// 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;
@@ -173,14 +185,18 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
current_analyzer_->framerateChanged();
}
SaveFramerate(new_framerate);
+
}
void AnalyzerContainer::Load() {
+
QSettings s;
s.beginGroup(kSettingsGroup);
+ QString type = s.value("type", "BlockAnalyzer").toString();
+ current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
+ s.endGroup();
// Analyzer
- QString type = s.value("type", "BlockAnalyzer").toString();
if (type.isEmpty()) {
DisableAnalyzer();
disable_action_->setChecked(true);
@@ -196,7 +212,6 @@ void AnalyzerContainer::Load() {
}
// Framerate
- current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
for (int i = 0; i < framerate_list_.count(); ++i) {
if (current_framerate_ == framerate_list_[i]) {
ChangeFramerate(current_framerate_);
@@ -204,27 +219,35 @@ void AnalyzerContainer::Load() {
break;
}
}
+
}
void AnalyzerContainer::SaveFramerate(int framerate) {
+
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
current_framerate_ = framerate;
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue(kSettingsFramerate, current_framerate_);
+ s.endGroup();
+
}
void AnalyzerContainer::Save() {
+
QSettings s;
s.beginGroup(kSettingsGroup);
-
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
+ s.endGroup();
+
}
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
+
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
mapper_framerate_->setMapping(action, framerate);
group_framerate_->addAction(action);
framerate_list_ << framerate;
action->setCheckable(true);
+
}
diff --git a/src/analyzer/analyzercontainer.h b/src/analyzer/analyzercontainer.h
index 597d534f..17872ddc 100644
--- a/src/analyzer/analyzercontainer.h
+++ b/src/analyzer/analyzercontainer.h
@@ -26,12 +26,12 @@
#include
#include
+#include
+#include
#include
#include
#include
#include
-#include
-#include
#include
#include
#include
@@ -120,5 +120,4 @@ void AnalyzerContainer::AddAnalyzerType() {
actions_ << action;
}
-#endif
-
+#endif // ANALYZERCONTAINER_H
diff --git a/src/analyzer/blockanalyzer.cpp b/src/analyzer/blockanalyzer.cpp
index 32b308b1..397892b1 100644
--- a/src/analyzer/blockanalyzer.cpp
+++ b/src/analyzer/blockanalyzer.cpp
@@ -1,13 +1,30 @@
-// Author: Max Howell , (C) 2003-5
-// Mark Kretschmann , (C) 2005
-// Copyright: See COPYING file that comes with this distribution
-//
+/*
+ Strawberry Music Player
+ This file was part of Amarok.
+ Copyright 2003-2005, Max Howell
+ Copyright 2005, Mark Kretschmann
+ Copyright 2009-2010, David Sansome
+ Copyright 2010, 2014, John Maguire
+
+ 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 .
+
+*/
#include "blockanalyzer.h"
#include
#include
-#include
#include
#include
@@ -19,12 +36,12 @@
#include "analyzerbase.h"
#include "fht.h"
-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");
@@ -34,18 +51,18 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
rows_(0),
y_(0),
barpixmap_(1, 1),
- topbarpixmap_(WIDTH, HEIGHT),
- scope_(MIN_COLUMNS),
+ topbarpixmap_(kWidth, kHeight),
+ scope_(kMinColumns),
store_(1 << 8, 0),
- fade_bars_(FADE_SIZE),
+ fade_bars_(kFadeSize),
fade_pos_(1 << 8, 50),
fade_intensity_(1 << 8, 32) {
- setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there
- setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
+ setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
+ 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) 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_;
// all is explained in analyze()..
- //+1 to counter -1 in maxSizes, trust me we need this!
- columns_ = qMax(uint(double(width() + 1) / (WIDTH + 1)), MAX_COLUMNS);
- rows_ = uint(double(height() + 1) / (HEIGHT + 1));
+ // +1 to counter -1 in maxSizes, trust me we need this!
+ 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
- y_ = (height() - (rows_ * (HEIGHT + 1)) + 2) / 2;
+ y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
scope_.resize(columns_);
if (rows_ != oldRows) {
- barpixmap_ = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
+ barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
- for (uint i = 0; i < FADE_SIZE; ++i)
- fade_bars_[i] = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
+ for (uint i = 0; i < kFadeSize; ++i)
+ fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 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;
- float* front = static_cast(&s.front());
-
- fht_->spectrum(front);
- fht_->scale(front, 1.0 / 20);
+ fht_->spectrum(s.data());
+ fht_->scale(s.data(), 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!
- 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
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 ((float)y > store_[x])
- y = int(store_[x] += step_);
+ // This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
+ if (static_cast(y) > store_[x])
+ y = static_cast(store_[x] += step_);
else
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 (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_intensity_[x] = FADE_SIZE;
+ fade_intensity_[x] = kFadeSize;
}
if (fade_intensity_[x] > 0) {
const uint offset = --fade_intensity_[x];
- const uint y = y_ + (fade_pos_[x] * (HEIGHT + 1));
- canvas_painter.drawPixmap(x * (WIDTH + 1), y, fade_bars_[offset], 0, 0, WIDTH, height() - y);
+ 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 (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
- canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, *bar(),
- 0, y * (HEIGHT + 1), bar()->width(),
- bar()->height());
+ canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
}
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(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
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
* @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 {
public:
- OutputOnExit(const QColor &color) : c(color) {}
+ explicit OutputOnExit(const QColor &color) : c(color) {}
~OutputOnExit() {
int 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;
};
-// hack so I don't have to cast everywhere
-#define amount static_cast(_amount)
- // #define STAMP debug() << (QValueList() << fh << fs << fv) << endl;
- // #define STAMP1( string ) debug() << string << ": " <<
- // (QValueList() << fh << fs << fv) << endl;
- // #define STAMP2( string, value ) debug() << string << "=" << value << ":
- // " << (QValueList() << fh << fs << fv) << endl;
-
OutputOnExit allocateOnTheStack(fg);
int bh, bs, bv;
@@ -255,23 +260,17 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
int dv = abs(bv - fv);
- // STAMP2( "DV", dv );
-
// value is the best measure of contrast
// if there is enough difference in value already, return fg unchanged
- if (dv > amount) return fg;
+ if (dv > static_cast(amount)) return fg;
int ds = abs(bs - fs);
- // STAMP2( "DS", ds );
-
// saturation is good enough too. But not as good. TODO adapt this a little
- if (ds > amount) return fg;
+ if (ds > static_cast(amount)) return fg;
int dh = abs(bh - fh);
- // STAMP2( "DH", dh );
-
if (dh > 120) {
// a third of the colour wheel automatically guarentees contrast
// 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
// provide sufficient contrast
- if (ds > amount / 2 && (bs > 125 && fs > 125))
- // STAMP1( "Sufficient saturation difference, and hues are
- // compliemtary" );
+ if (ds > static_cast(amount) / 2 && (bs > 125 && fs > 125))
return fg;
- else if (dv > amount / 2 && (bv > 125 && fv > 125))
- // STAMP1( "Sufficient value difference, and hues are
- // compliemtary" );
+ else if (dv > static_cast(amount) / 2 && (bv > 125 && fv > 125))
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) {
// low saturation on a low saturation is sad
const int tmp = 50 - fs;
fs = 50;
- if (amount > tmp)
- _amount -= tmp;
+ if (static_cast(amount) > tmp)
+ amount -= tmp;
else
- _amount = 0;
+ amount = 0;
}
// test that there is available value to honor our contrast requirement
- if (255 - dv < amount) {
+ if (255 - dv < static_cast(amount)) {
// we have to modify the value and saturation of fg
// adjustToLimits( bv, fv, amount );
-
- // STAMP
-
// see if we need to adjust the saturation
- if (amount > 0) adjustToLimits(bs, fs, _amount);
-
- // STAMP
+ if (static_cast(amount) > 0) adjustToLimits(bs, fs, amount);
// see if we need to adjust the hue
- if (amount > 0) fh += amount; // cycles around;
-
- // STAMP
+ if (static_cast(amount) > 0)
+ fh += static_cast(amount); // cycles around;
return QColor::fromHsv(fh, fs, fv);
}
- // STAMP
+ if (fv > bv && bv > static_cast(amount))
+ return QColor::fromHsv(fh, fs, bv - static_cast(amount));
- if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount);
+ if (fv < bv && fv > static_cast(amount))
+ return QColor::fromHsv(fh, fs, fv - static_cast(amount));
- // STAMP
+ if (fv > bv && (255 - fv > static_cast(amount)))
+ return QColor::fromHsv(fh, fs, fv + static_cast(amount));
- if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - 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";
+ if (fv < bv && (255 - bv > static_cast(amount)))
+ return QColor::fromHsv(fh, fs, bv + static_cast(amount));
return Qt::blue;
-
-#undef amount
- // #undef STAMP
-
}
void BlockAnalyzer::paletteChange(const QPalette&) {
@@ -361,17 +330,17 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
topbarpixmap_.fill(fg);
- const double dr = 15 * double(bg.red() - fg.red()) / (rows_ * 16);
- const double dg = 15 * double(bg.green() - fg.green()) / (rows_ * 16);
- const double db = 15 * double(bg.blue() - fg.blue()) / (rows_ * 16);
+ 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; (uint)y < 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 + int(dr * y), g + int(dg * y), b + int(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);
@@ -388,12 +357,12 @@ 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) {
+ for (uint y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Background));
QPainter f(&fade_bars_[y]);
- for (int z = 0; (uint)z < rows_; ++z) {
- const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
- f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * Y), g + int(dg * Y), b + int(db * 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)));
}
}
}
@@ -404,14 +373,21 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
void BlockAnalyzer::drawBackground() {
+ if (background_.isNull()) {
+ return;
+ }
+
const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark(112);
background_.fill(bg);
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 * (WIDTH + 1), y * (HEIGHT + 1) + y_, WIDTH, HEIGHT, bgdark);
+ p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
}
diff --git a/src/analyzer/blockanalyzer.h b/src/analyzer/blockanalyzer.h
index be4dfe33..7d65ed63 100644
--- a/src/analyzer/blockanalyzer.h
+++ b/src/analyzer/blockanalyzer.h
@@ -1,6 +1,25 @@
-// Maintainer: Max Howell , (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
+ Copyright 2005, Mark Kretschmann
+ Copyright 2009-2010, David Sansome
+ Copyright 2010, 2014, John Maguire
+
+ 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 .
+
+*/
#ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H
@@ -11,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -21,22 +41,18 @@
class QResizeEvent;
-/**
- * @author Max Howell
- */
-
class BlockAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
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;
@@ -53,22 +69,21 @@ class BlockAnalyzer : public Analyzer::Base {
private:
QPixmap *bar() { return &barpixmap_; }
- uint columns_, rows_; // number of rows and columns of blocks
- uint y_; // y-offset from top of widget
+ 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 scope_; // so we don't create a vector every frame
- std::vector store_; // current bar heights
- std::vector yscale_;
+ Analyzer::Scope scope_; // so we don't create a vector every frame
+ QVector store_; // current bar heights
+ QVector yscale_;
- // FIXME why can't I namespace these? c++ issue?
- std::vector fade_bars_;
- std::vector fade_pos_;
- std::vector fade_intensity_;
+ QVector fade_bars_;
+ QVector fade_pos_;
+ QVector fade_intensity_;
float step_; // rows to fall per frame
};
-#endif
+#endif // BLOCKANALYZER_H
diff --git a/src/analyzer/boomanalyzer.cpp b/src/analyzer/boomanalyzer.cpp
new file mode 100644
index 00000000..aaabac4e
--- /dev/null
+++ b/src/analyzer/boomanalyzer.cpp
@@ -0,0 +1,169 @@
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2004, Max Howell
+ Copyright 2009-2010, David Sansome
+ Copyright 2010, 2014, John Maguire
+ Copyright 2014-2015, Mark Furneaux
+ Copyright 2014, Krzysztof Sobiecki
+
+ 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 .
+
+*/
+
+#include "boomanalyzer.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+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(newValue) / 1000;
+}
+
+void BoomAnalyzer::changeF_peakSpeed(int newValue) {
+ F_peakSpeed_ = static_cast(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(static_cast(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
+ scope_.resize(bands_);
+
+ 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_);
+ 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, 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_);
+
+}
+
diff --git a/src/analyzer/boomanalyzer.h b/src/analyzer/boomanalyzer.h
new file mode 100644
index 00000000..fe2cc1a0
--- /dev/null
+++ b/src/analyzer/boomanalyzer.h
@@ -0,0 +1,75 @@
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2004, Max Howell
+ Copyright 2009-2010, David Sansome
+ Copyright 2010, 2014, John Maguire
+ Copyright 2014-2015, Mark Furneaux
+ Copyright 2014, Krzysztof Sobiecki
+
+ 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 .
+
+*/
+
+#ifndef BOOMANALYZER_H
+#define BOOMANALYZER_H
+
+#include "analyzerbase.h"
+
+#include
+#include
+#include
+#include
+#include
+
+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 bar_height_;
+ std::vector peak_height_;
+ std::vector peak_speed_;
+
+ QPixmap barPixmap_;
+ QPixmap canvas_;
+
+};
+
+#endif // BOOMANALYZER_H
diff --git a/src/analyzer/fht.cpp b/src/analyzer/fht.cpp
index 50206961..2c6a04e6 100644
--- a/src/analyzer/fht.cpp
+++ b/src/analyzer/fht.cpp
@@ -1,140 +1,140 @@
-// FHT - Fast Hartley Transform Class
-//
-// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// 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$
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2004, Melchior FRANZ
+ Copyright 2010, 2014, John Maguire
+ Copyright 2014, Krzysztof Sobiecki
+ Copyright 2017, Santiago Gil
-#include
-#include
+ 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 .
+*/
#include "fht.h"
-FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
- if (n < 3) {
- m_num = 0;
- m_exp2 = -1;
- return;
- }
- m_exp2 = n;
- m_num = 1 << n;
+#include
+#include
+
+#include
+
+FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
if (n > 3) {
- m_buf = new float[m_num];
- m_tab = new float[m_num * 2];
+ buf_vector_.resize(num_);
+ tab_vector_.resize(num_ * 2);
makeCasTable();
}
}
-FHT::~FHT() {
- delete[] m_buf;
- delete[] m_tab;
- delete[] m_log;
-}
+FHT::~FHT() {}
+
+int FHT::sizeExp() const { return exp2_; }
+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) {
- float d, *costab, *sintab;
- int ul, ndiv2 = m_num / 2;
+ float* costab = tab_();
+ float* sintab = tab_() + num_ / 2 + 1;
- for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
- d = M_PI * ul / ndiv2;
+ for (int ul = 0; ul < num_; ul++) {
+ float d = M_PI * ul / (num_ / 2);
*costab = *sintab = cos(d);
- costab += 2, sintab += 2;
- if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
+ costab += 2;
+ 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) {
- 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];
- float f = n / log10((double)n);
- for (i = 0, r = m_log; i < n; i++, r++) {
- j = int(rint(log10(i + 1.0) * f));
+
+ int n = num_ / 2, i, j, k, *r;
+ if (log_vector_.size() < n) {
+ log_vector_.resize(n);
+ float f = n / log10(static_cast(n));
+ 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)
+ if (i == j) {
*out++ = p[i];
+ }
else {
float base = p[k - 1];
float step = (p[j] - base) / (j - (k - 1));
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
}
}
+
}
void FHT::semiLogSpectrum(float* p) {
- float e;
power2(p);
- for (int i = 0; i < (m_num / 2); i++, p++) {
- e = 10.0 * log10(sqrt(*p * .5));
+ for (int i = 0; i < (num_ / 2); i++, p++) {
+ float e = 10.0 * log10(sqrt(*p / 2));
*p = e < 0 ? 0 : e;
}
}
void FHT::spectrum(float* 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(sqrt(*p / 2));
}
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++ /= 2;
}
void FHT::power2(float* p) {
- int i;
- float* q;
- _transform(p, m_num, 0);
+ _transform(p, num_, 0);
- *p = (*p * *p), *p += *p, p++;
+ *p = static_cast(2 * pow(*p, 2));
+ p++;
- for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
- *p = (*p * *p) + (*q * *q), p++;
+ float* q = p + num_ - 2;
+ for (int i = 1; i < (num_ / 2); i++) {
+ *p = static_cast(pow(*p, 2) + pow(*q, 2));
+ p++;
+ q--;
+ }
}
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) {
+
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;
@@ -159,6 +159,7 @@ void FHT::transform8(float* p) {
*--p = a_ce_g + b_df_h;
*--p = ac_e_g + b_f2;
*--p = aceg + bdfh;
+
}
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;
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);
+ std::copy(buf_(), buf_() + n, p + k);
_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++;
@@ -200,5 +201,7 @@ void FHT::_transform(float* p, int n, int k) {
*t1++ = *pp + a;
*t2++ = *pp++ - a;
}
- memcpy(p + k, m_buf, sizeof(float) * n);
+
+ std::copy(buf_(), buf_() + n, p + k);
+
}
diff --git a/src/analyzer/fht.h b/src/analyzer/fht.h
index 73c5daa6..5b79679b 100644
--- a/src/analyzer/fht.h
+++ b/src/analyzer/fht.h
@@ -1,26 +1,30 @@
-// FHT - Fast Hartley Transform Class
-//
-// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// 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$
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2004, Melchior FRANZ
+ Copyright 2010, 2014, John Maguire
+ Copyright 2014, Krzysztof Sobiecki
+ 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 .
+*/
#ifndef FHT_H
#define FHT_H
+#include
+
/**
* Implementation of the Hartley Transform after Bracewell's discrete
* 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
*/
class FHT {
- int m_exp2;
- int m_num;
- float* m_buf;
- float* m_tab;
- int* m_log;
+ const int num_;
+ const int exp2_;
+
+ QVector buf_vector_;
+ QVector tab_vector_;
+ QVector log_vector_;
+
+ float* buf_();
+ float* tab_();
+ int* log_();
/**
* Create a table of "cas" (cosine and sine) values.
@@ -57,10 +66,8 @@ class FHT {
FHT(int);
~FHT();
- inline int sizeExp() const { return m_exp2; }
- inline int size() const { return m_num; }
- float* copy(float*, float*);
- float* clear(float*);
+ int sizeExp() const;
+ int size() const;
void scale(float*, float);
/**
@@ -115,4 +122,4 @@ class FHT {
void transform(float*);
};
-#endif
+#endif // FHT_H
diff --git a/src/analyzer/rainbowanalyzer.cpp b/src/analyzer/rainbowanalyzer.cpp
new file mode 100644
index 00000000..740a87ee
--- /dev/null
+++ b/src/analyzer/rainbowanalyzer.cpp
@@ -0,0 +1,207 @@
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2011, Tyler Rhodes
+ Copyright 2011-2012, 2014, David Sansome
+ Copyright 2011, 2014, John Maguire
+ Copyright 2014, Alibek Omarov
+ Copyright 2014, Krzysztof Sobiecki
+ Copyright 2014-2015, Mark Furneaux
+ Copyright 2015, Arun Narayanankutty
+
+ 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 .
+*/
+
+#include "rainbowanalyzer.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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(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(height()) / 2 - static_cast(kRainbowHeight[rainbowtype]) / 2;
+ for (int band = 0; band < kRainbowBands; ++band) {
+ // Calculate the Y position of this band.
+ const float y = static_cast(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) {}
diff --git a/src/analyzer/rainbowanalyzer.h b/src/analyzer/rainbowanalyzer.h
new file mode 100644
index 00000000..ce65e631
--- /dev/null
+++ b/src/analyzer/rainbowanalyzer.h
@@ -0,0 +1,141 @@
+/*
+ Strawberry Music Player
+ This file was part of Clementine.
+ Copyright 2011, Tyler Rhodes
+ Copyright 2011-2012, 2014, David Sansome
+ Copyright 2011, 2014, John Maguire
+ Copyright 2014, Alibek Omarov
+ Copyright 2014, Krzysztof Sobiecki
+ Copyright 2014-2015, Mark Furneaux
+ Copyright 2015, Arun Narayanankutty
+
+ 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 .
+*/
+
+#ifndef RAINBOWANALYZER_H
+#define RAINBOWANALYZER_H
+
+#include "analyzerbase.h"
+
+#include
+#include
+#include
+#include
+#include
+
+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