Add boom and rainbow analyzers
This commit is contained in:
parent
ad7084b897
commit
0679b78c1d
@ -29,6 +29,8 @@
|
||||
<file>pictures/osd_shadow_corner.png</file>
|
||||
<file>pictures/osd_shadow_edge.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>
|
||||
</RCC>
|
||||
|
BIN
data/pictures/nyancat.png
Normal file
BIN
data/pictures/nyancat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 850 B |
BIN
data/pictures/rainbowdash.png
Normal file
BIN
data/pictures/rainbowdash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
34
dist/debian/copyright
vendored
34
dist/debian/copyright
vendored
@ -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 <jonas@jkvinge.net>
|
||||
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 <mfranz@kde.org>
|
||||
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2017, Santiago Gil
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/analyzer/analyzerbase.cpp
|
||||
src/analyzer/analyzerbase.h
|
||||
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+
|
||||
|
||||
Files: src/analyzer/blockanalyzer.cpp
|
||||
src/analyzer/blockanalyzer.h
|
||||
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
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+
|
||||
|
||||
Files: src/collection/savedgroupingmanager.h
|
||||
|
@ -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
|
||||
|
@ -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 <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
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QTimerEvent>
|
||||
#include <QtEvents>
|
||||
|
||||
#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 <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
|
||||
|
||||
// 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<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() );
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
#define ANALYZERBASE_H
|
||||
@ -7,7 +26,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
@ -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
|
||||
|
@ -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<BlockAnalyzer>();
|
||||
AddAnalyzerType<BoomAnalyzer>();
|
||||
AddAnalyzerType<Rainbow::NyanCatAnalyzer>();
|
||||
AddAnalyzerType<Rainbow::RainbowDashAnalyzer>();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -26,12 +26,12 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QPoint>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QSignalMapper>
|
||||
#include <QTimer>
|
||||
#include <QtEvents>
|
||||
@ -120,5 +120,4 @@ void AnalyzerContainer::AddAnalyzerType() {
|
||||
actions_ << action;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ANALYZERCONTAINER_H
|
||||
|
@ -1,13 +1,30 @@
|
||||
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003-5
|
||||
// Mark Kretschmann <markey@web.de>, (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 <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 <cstdlib>
|
||||
#include <cmath>
|
||||
#include <scoped_allocator>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
@ -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<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
|
||||
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||
|
||||
// this is the y-offset for drawing from the top of the widget
|
||||
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<float*>(&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<float>(y) > store_[x])
|
||||
y = static_cast<int>(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<int>(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<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);
|
||||
|
||||
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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(amount) > 0)
|
||||
fh += static_cast<int>(amount); // cycles around;
|
||||
|
||||
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);
|
||||
|
||||
// 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<int>(amount)))
|
||||
return QColor::fromHsv(fh, fs, bv + static_cast<int>(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<double>(bg.red() - fg.red()) / (rows_ * 16);
|
||||
const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
|
||||
const double db = 15 * static_cast<double>(bg.blue() - fg.blue()) / (rows_ * 16);
|
||||
const int r = fg.red(), g = fg.green(), b = fg.blue();
|
||||
|
||||
bar()->fill(bg);
|
||||
|
||||
QPainter p(bar());
|
||||
for (int y = 0; (uint)y < rows_; ++y)
|
||||
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
|
||||
// graduate the fg color
|
||||
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + 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);
|
||||
@ -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<uint>(z) < rows_; ++z) {
|
||||
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
|
||||
f.fillRect(0, z * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y), b + static_cast<int>(db * Y)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
#define BLOCKANALYZER_H
|
||||
@ -11,6 +30,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
@ -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<float> store_; // current bar heights
|
||||
std::vector<float> yscale_;
|
||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||
QVector<float> store_; // current bar heights
|
||||
QVector<float> yscale_;
|
||||
|
||||
// FIXME why can't I namespace these? c++ issue?
|
||||
std::vector<QPixmap> fade_bars_;
|
||||
std::vector<uint> fade_pos_;
|
||||
std::vector<int> fade_intensity_;
|
||||
QVector<QPixmap> fade_bars_;
|
||||
QVector<uint> fade_pos_;
|
||||
QVector<int> fade_intensity_;
|
||||
|
||||
float step_; // rows to fall per frame
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // BLOCKANALYZER_H
|
||||
|
169
src/analyzer/boomanalyzer.cpp
Normal file
169
src/analyzer/boomanalyzer.cpp
Normal 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_);
|
||||
|
||||
}
|
||||
|
75
src/analyzer/boomanalyzer.h
Normal file
75
src/analyzer/boomanalyzer.h
Normal 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
|
@ -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 <mfranz@kde.org>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2017, Santiago Gil
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
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 "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 <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QVector>
|
||||
|
||||
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<double>(n));
|
||||
for (i = 0, r = log_(); i < n; i++, r++) {
|
||||
j = static_cast<int>(rint(log10(i + 1.0) * f));
|
||||
*r = j >= n ? n - 1 : j;
|
||||
}
|
||||
}
|
||||
semiLogSpectrum(p);
|
||||
*out++ = *p = *p / 100;
|
||||
for (k = i = 1, r = m_log; i < n; i++) {
|
||||
for (k = i = 1, r = log_(); i < n; i++) {
|
||||
j = *r++;
|
||||
if (i == j)
|
||||
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<float>(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<float>(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<float>(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);
|
||||
|
||||
}
|
||||
|
@ -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 <mfranz@kde.org>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@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 FHT_H
|
||||
#define FHT_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
/**
|
||||
* 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<float> buf_vector_;
|
||||
QVector<float> tab_vector_;
|
||||
QVector<int> 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
|
||||
|
207
src/analyzer/rainbowanalyzer.cpp
Normal file
207
src/analyzer/rainbowanalyzer.cpp
Normal 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) {}
|
141
src/analyzer/rainbowanalyzer.h
Normal file
141
src/analyzer/rainbowanalyzer.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user