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_corner.png</file>
|
||||||
<file>pictures/osd_shadow_edge.png</file>
|
<file>pictures/osd_shadow_edge.png</file>
|
||||||
<file>pictures/deezer.png</file>
|
<file>pictures/deezer.png</file>
|
||||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
<file>pictures/nyancat.png</file>
|
||||||
|
<file>pictures/rainbowdash.png</file>
|
||||||
|
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 850 B |
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -59,8 +59,8 @@ Files: src/core/main.h
|
||||||
src/scrobbler/*
|
src/scrobbler/*
|
||||||
src/tidal/*
|
src/tidal/*
|
||||||
src/deezer/*
|
src/deezer/*
|
||||||
transcoderoptionswavpack.cpp
|
src/transcoder/transcoderoptionswavpack.cpp
|
||||||
transcoderoptionswavpack.h
|
src/transcoder/transcoderoptionswavpack.h
|
||||||
Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net>
|
Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
|
@ -195,18 +195,46 @@ License: GPL-3+
|
||||||
|
|
||||||
Files: src/analyzer/fht.cpp
|
Files: src/analyzer/fht.cpp
|
||||||
src/analyzer/fht.h
|
src/analyzer/fht.h
|
||||||
Copyright: 2004, Melchior FRANZ - mfranz@kde.org
|
Copyright: 2004, Melchior FRANZ <mfranz@kde.org>
|
||||||
|
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
2017, Santiago Gil
|
||||||
License: GPL-2+
|
License: GPL-2+
|
||||||
|
|
||||||
Files: src/analyzer/analyzerbase.cpp
|
Files: src/analyzer/analyzerbase.cpp
|
||||||
src/analyzer/analyzerbase.h
|
src/analyzer/analyzerbase.h
|
||||||
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||||
|
2009-2012, David Sansome <me@davidsansome.com>
|
||||||
|
2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
2017, Santiago Gil
|
||||||
License: GPL-2+
|
License: GPL-2+
|
||||||
|
|
||||||
Files: src/analyzer/blockanalyzer.cpp
|
Files: src/analyzer/blockanalyzer.cpp
|
||||||
src/analyzer/blockanalyzer.h
|
src/analyzer/blockanalyzer.h
|
||||||
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||||
2005, Mark Kretschmann <markey@web.de>
|
2005, Mark Kretschmann <markey@web.de>
|
||||||
|
2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
License: GPL-2+
|
||||||
|
|
||||||
|
Files: src/analyzer/boomanalyzer.cpp
|
||||||
|
src/analyzer/boomanalyzer.h
|
||||||
|
Copyright: 2004, Max Howell <max.howell@methylblue.com>
|
||||||
|
2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||||
|
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
License: GPL-2+
|
||||||
|
|
||||||
|
Files: src/analyzer/rainbowanalyzer.cpp
|
||||||
|
src/analyzer/rainbowanalyzer.h
|
||||||
|
Copyright: 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
|
||||||
|
2011-2012, 2014, David Sansome <me@davidsansome.com>
|
||||||
|
2011, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||||
|
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||||
|
2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
|
||||||
License: GPL-2+
|
License: GPL-2+
|
||||||
|
|
||||||
Files: src/collection/savedgroupingmanager.h
|
Files: src/collection/savedgroupingmanager.h
|
||||||
|
|
|
@ -125,6 +125,8 @@ set(SOURCES
|
||||||
analyzer/analyzerbase.cpp
|
analyzer/analyzerbase.cpp
|
||||||
analyzer/analyzercontainer.cpp
|
analyzer/analyzercontainer.cpp
|
||||||
analyzer/blockanalyzer.cpp
|
analyzer/blockanalyzer.cpp
|
||||||
|
analyzer/boomanalyzer.cpp
|
||||||
|
analyzer/rainbowanalyzer.cpp
|
||||||
|
|
||||||
equalizer/equalizer.cpp
|
equalizer/equalizer.cpp
|
||||||
equalizer/equalizerslider.cpp
|
equalizer/equalizerslider.cpp
|
||||||
|
@ -307,6 +309,8 @@ set(HEADERS
|
||||||
analyzer/analyzerbase.h
|
analyzer/analyzerbase.h
|
||||||
analyzer/analyzercontainer.h
|
analyzer/analyzercontainer.h
|
||||||
analyzer/blockanalyzer.h
|
analyzer/blockanalyzer.h
|
||||||
|
analyzer/boomanalyzer.h
|
||||||
|
analyzer/rainbowanalyzer.h
|
||||||
|
|
||||||
equalizer/equalizer.h
|
equalizer/equalizer.h
|
||||||
equalizer/equalizerslider.h
|
equalizer/equalizerslider.h
|
||||||
|
|
|
@ -1,32 +1,40 @@
|
||||||
/***************************************************************************
|
/*
|
||||||
viswidget.cpp - description
|
Strawberry Music Player
|
||||||
-------------------
|
This file was part of Amarok.
|
||||||
begin : Die Jan 7 2003
|
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
|
||||||
copyright : (C) 2003 by Max Howell
|
Copyright 2009-2012, David Sansome <me@davidsansome.com>
|
||||||
email : markey@web.de
|
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
***************************************************************************/
|
Copyright 2017, Santiago Gil
|
||||||
|
|
||||||
/***************************************************************************
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
* *
|
it under the terms of the GNU General Public License as published by
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
* it under the terms of the GNU General Public License as published by *
|
(at your option) any later version.
|
||||||
* the Free Software Foundation; either version 2 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
* *
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
***************************************************************************/
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QVector>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QTimerEvent>
|
#include <QTimerEvent>
|
||||||
#include <QtEvents>
|
#include <QtEvents>
|
||||||
|
|
||||||
#include "analyzerbase.h"
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
|
@ -36,10 +44,11 @@
|
||||||
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it
|
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it
|
||||||
// 4. if you want to manipulate the scope, reimplement transform()
|
// 4. if you want to manipulate the scope, reimplement transform()
|
||||||
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
|
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
|
||||||
// TODO make an INSTRUCTIONS file
|
//
|
||||||
|
// TODO:
|
||||||
|
// Make an INSTRUCTIONS file
|
||||||
// can't mod scope in analyze you have to use transform
|
// can't mod scope in analyze you have to use transform
|
||||||
|
// for 2D use setErasePixmap Qt function insetead of m_background
|
||||||
// TODO for 2D use setErasePixmap Qt function insetead of m_background
|
|
||||||
|
|
||||||
// make the linker happy only for gcc < 4.0
|
// make the linker happy only for gcc < 4.0
|
||||||
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
|
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
|
||||||
|
@ -53,7 +62,6 @@ Analyzer::Base::Base(QWidget *parent, uint scopeSize)
|
||||||
fht_(new FHT(scopeSize)),
|
fht_(new FHT(scopeSize)),
|
||||||
engine_(nullptr),
|
engine_(nullptr),
|
||||||
lastscope_(512),
|
lastscope_(512),
|
||||||
current_chunk_(0),
|
|
||||||
new_frame_(false),
|
new_frame_(false),
|
||||||
is_playing_(false) {}
|
is_playing_(false) {}
|
||||||
|
|
||||||
|
@ -63,19 +71,18 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
|
||||||
|
|
||||||
void Analyzer::Base::transform(Scope& scope) {
|
void Analyzer::Base::transform(Scope& scope) {
|
||||||
|
|
||||||
// This is a standard transformation that should give an FFT scope that has bands for pretty analyzers
|
QVector<float> aux(fht_->size());
|
||||||
|
if (aux.size() >= scope.size()) {
|
||||||
|
std::copy(scope.begin(), scope.end(), aux.begin());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::copy(scope.begin(), scope.begin() + aux.size(), aux.begin());
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Resizing here is redundant as FHT routines only calculate FHT::size() values scope.resize( fht_->size() );
|
fht_->logSpectrum(scope.data(), aux.data());
|
||||||
|
fht_->scale(scope.data(), 1.0 / 20);
|
||||||
float *front = static_cast<float*>(&scope.front());
|
|
||||||
|
|
||||||
float *f = new float[fht_->size()];
|
|
||||||
fht_->copy(&f[0], front);
|
|
||||||
fht_->logSpectrum(front, &f[0]);
|
|
||||||
fht_->scale(front, 1.0 / 20);
|
|
||||||
|
|
||||||
scope.resize(fht_->size() / 2); // second half of values are rubbish
|
scope.resize(fht_->size() / 2); // second half of values are rubbish
|
||||||
delete[] f;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004
|
/*
|
||||||
// Copyright: See COPYING file that comes with this distribution
|
Strawberry Music Player
|
||||||
|
This file was part of Amarok.
|
||||||
|
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
|
||||||
|
Copyright 2009-2012, David Sansome <me@davidsansome.com>
|
||||||
|
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
Copyright 2017, Santiago Gil
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ANALYZERBASE_H
|
#ifndef ANALYZERBASE_H
|
||||||
#define ANALYZERBASE_H
|
#define ANALYZERBASE_H
|
||||||
|
@ -7,7 +26,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
#include <sys/types.h>
|
# include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -23,6 +42,7 @@
|
||||||
|
|
||||||
#include "analyzer/fht.h"
|
#include "analyzer/fht.h"
|
||||||
#include "engine/engine_fwd.h"
|
#include "engine/engine_fwd.h"
|
||||||
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
class QHideEvent;
|
class QHideEvent;
|
||||||
class QShowEvent;
|
class QShowEvent;
|
||||||
|
@ -54,7 +74,7 @@ class Base : public QWidget {
|
||||||
virtual void framerateChanged() {}
|
virtual void framerateChanged() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Base(QWidget*, uint scopeSize = 7);
|
explicit Base(QWidget*, uint scopeSize = 7);
|
||||||
|
|
||||||
void hideEvent(QHideEvent*);
|
void hideEvent(QHideEvent*);
|
||||||
void showEvent(QShowEvent*);
|
void showEvent(QShowEvent*);
|
||||||
|
@ -76,7 +96,7 @@ class Base : public QWidget {
|
||||||
FHT *fht_;
|
FHT *fht_;
|
||||||
EngineBase *engine_;
|
EngineBase *engine_;
|
||||||
Scope lastscope_;
|
Scope lastscope_;
|
||||||
int current_chunk_;
|
|
||||||
bool new_frame_;
|
bool new_frame_;
|
||||||
bool is_playing_;
|
bool is_playing_;
|
||||||
};
|
};
|
||||||
|
@ -84,6 +104,7 @@ class Base : public QWidget {
|
||||||
void interpolate(const Scope&, Scope&);
|
void interpolate(const Scope&, Scope&);
|
||||||
void initSin(Scope&, const uint = 6000);
|
void initSin(Scope&, const uint = 6000);
|
||||||
|
|
||||||
} // END namespace Analyzer
|
} // namespace Analyzer
|
||||||
|
|
||||||
|
#endif // ANALYZERBASE_H
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
|
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
#include "blockanalyzer.h"
|
#include "blockanalyzer.h"
|
||||||
|
#include "boomanalyzer.h"
|
||||||
|
#include "rainbowanalyzer.h"
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
ignore_next_click_(false),
|
ignore_next_click_(false),
|
||||||
current_analyzer_(nullptr),
|
current_analyzer_(nullptr),
|
||||||
engine_(nullptr) {
|
engine_(nullptr) {
|
||||||
|
|
||||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
@ -79,6 +82,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
|
|
||||||
AddAnalyzerType<BlockAnalyzer>();
|
AddAnalyzerType<BlockAnalyzer>();
|
||||||
|
AddAnalyzerType<BoomAnalyzer>();
|
||||||
|
AddAnalyzerType<Rainbow::NyanCatAnalyzer>();
|
||||||
|
AddAnalyzerType<Rainbow::RainbowDashAnalyzer>();
|
||||||
|
|
||||||
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
|
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
|
||||||
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
|
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
|
||||||
|
@ -93,6 +99,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
connect(double_click_timer_, SIGNAL(timeout()), SLOT(ShowPopupMenu()));
|
connect(double_click_timer_, SIGNAL(timeout()), SLOT(ShowPopupMenu()));
|
||||||
|
|
||||||
Load();
|
Load();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::SetActions(QAction *visualisation) {
|
void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||||
|
@ -101,6 +108,7 @@ void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
|
||||||
if (e->button() == Qt::LeftButton) {
|
if (e->button() == Qt::LeftButton) {
|
||||||
if (ignore_next_click_) {
|
if (ignore_next_click_) {
|
||||||
ignore_next_click_ = false;
|
ignore_next_click_ = false;
|
||||||
|
@ -114,6 +122,7 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
else if (e->button() == Qt::RightButton) {
|
else if (e->button() == Qt::RightButton) {
|
||||||
context_menu_->popup(e->globalPos());
|
context_menu_->popup(e->globalPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::ShowPopupMenu() {
|
void AnalyzerContainer::ShowPopupMenu() {
|
||||||
|
@ -144,6 +153,7 @@ void AnalyzerContainer::DisableAnalyzer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::ChangeAnalyzer(int id) {
|
void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||||
|
|
||||||
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
|
@ -161,9 +171,11 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||||
layout()->addWidget(current_analyzer_);
|
layout()->addWidget(current_analyzer_);
|
||||||
|
|
||||||
Save();
|
Save();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
||||||
|
|
||||||
if (current_analyzer_) {
|
if (current_analyzer_) {
|
||||||
// Even if it is not supposed to happen, I don't want to get a dbz error
|
// Even if it is not supposed to happen, I don't want to get a dbz error
|
||||||
new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate;
|
new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate;
|
||||||
|
@ -173,14 +185,18 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
||||||
current_analyzer_->framerateChanged();
|
current_analyzer_->framerateChanged();
|
||||||
}
|
}
|
||||||
SaveFramerate(new_framerate);
|
SaveFramerate(new_framerate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::Load() {
|
void AnalyzerContainer::Load() {
|
||||||
|
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
|
QString type = s.value("type", "BlockAnalyzer").toString();
|
||||||
|
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
// Analyzer
|
// Analyzer
|
||||||
QString type = s.value("type", "BlockAnalyzer").toString();
|
|
||||||
if (type.isEmpty()) {
|
if (type.isEmpty()) {
|
||||||
DisableAnalyzer();
|
DisableAnalyzer();
|
||||||
disable_action_->setChecked(true);
|
disable_action_->setChecked(true);
|
||||||
|
@ -196,7 +212,6 @@ void AnalyzerContainer::Load() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Framerate
|
// Framerate
|
||||||
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
|
||||||
for (int i = 0; i < framerate_list_.count(); ++i) {
|
for (int i = 0; i < framerate_list_.count(); ++i) {
|
||||||
if (current_framerate_ == framerate_list_[i]) {
|
if (current_framerate_ == framerate_list_[i]) {
|
||||||
ChangeFramerate(current_framerate_);
|
ChangeFramerate(current_framerate_);
|
||||||
|
@ -204,27 +219,35 @@ void AnalyzerContainer::Load() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::SaveFramerate(int framerate) {
|
void AnalyzerContainer::SaveFramerate(int framerate) {
|
||||||
|
|
||||||
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
||||||
current_framerate_ = framerate;
|
current_framerate_ = framerate;
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
s.setValue(kSettingsFramerate, current_framerate_);
|
s.setValue(kSettingsFramerate, current_framerate_);
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::Save() {
|
void AnalyzerContainer::Save() {
|
||||||
|
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
|
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
||||||
|
|
||||||
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
||||||
mapper_framerate_->setMapping(action, framerate);
|
mapper_framerate_->setMapping(action, framerate);
|
||||||
group_framerate_->addAction(action);
|
group_framerate_->addAction(action);
|
||||||
framerate_list_ << framerate;
|
framerate_list_ << framerate;
|
||||||
action->setCheckable(true);
|
action->setCheckable(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,12 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
#include <QList>
|
|
||||||
#include <QString>
|
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QtEvents>
|
#include <QtEvents>
|
||||||
|
@ -120,5 +120,4 @@ void AnalyzerContainer::AddAnalyzerType() {
|
||||||
actions_ << action;
|
actions_ << action;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // ANALYZERCONTAINER_H
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003-5
|
/*
|
||||||
// Mark Kretschmann <markey@web.de>, (C) 2005
|
Strawberry Music Player
|
||||||
// Copyright: See COPYING file that comes with this distribution
|
This file was part of Amarok.
|
||||||
//
|
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||||
|
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#include "blockanalyzer.h"
|
#include "blockanalyzer.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <scoped_allocator>
|
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
@ -19,12 +36,12 @@
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
|
|
||||||
const uint BlockAnalyzer::HEIGHT = 2;
|
const uint BlockAnalyzer::kHeight = 2;
|
||||||
const uint BlockAnalyzer::WIDTH = 4;
|
const uint BlockAnalyzer::kWidth = 4;
|
||||||
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary
|
const uint BlockAnalyzer::kMinRows = 3; // arbituary
|
||||||
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
|
const uint BlockAnalyzer::kMinColumns = 32; // arbituary
|
||||||
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
|
const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
|
||||||
const uint BlockAnalyzer::FADE_SIZE = 90;
|
const uint BlockAnalyzer::kFadeSize = 90;
|
||||||
|
|
||||||
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||||
|
|
||||||
|
@ -34,18 +51,18 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
|
||||||
rows_(0),
|
rows_(0),
|
||||||
y_(0),
|
y_(0),
|
||||||
barpixmap_(1, 1),
|
barpixmap_(1, 1),
|
||||||
topbarpixmap_(WIDTH, HEIGHT),
|
topbarpixmap_(kWidth, kHeight),
|
||||||
scope_(MIN_COLUMNS),
|
scope_(kMinColumns),
|
||||||
store_(1 << 8, 0),
|
store_(1 << 8, 0),
|
||||||
fade_bars_(FADE_SIZE),
|
fade_bars_(kFadeSize),
|
||||||
fade_pos_(1 << 8, 50),
|
fade_pos_(1 << 8, 50),
|
||||||
fade_intensity_(1 << 8, 32) {
|
fade_intensity_(1 << 8, 32) {
|
||||||
|
|
||||||
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there
|
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
|
||||||
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
|
||||||
|
|
||||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||||
for (uint i = 0; i < FADE_SIZE; ++i) fade_bars_[i] = QPixmap(1, 1);
|
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,20 +78,20 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
const uint oldRows = rows_;
|
const uint oldRows = rows_;
|
||||||
|
|
||||||
// all is explained in analyze()..
|
// all is explained in analyze()..
|
||||||
//+1 to counter -1 in maxSizes, trust me we need this!
|
// +1 to counter -1 in maxSizes, trust me we need this!
|
||||||
columns_ = qMax(uint(double(width() + 1) / (WIDTH + 1)), MAX_COLUMNS);
|
columns_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
|
||||||
rows_ = uint(double(height() + 1) / (HEIGHT + 1));
|
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||||
|
|
||||||
// this is the y-offset for drawing from the top of the widget
|
// this is the y-offset for drawing from the top of the widget
|
||||||
y_ = (height() - (rows_ * (HEIGHT + 1)) + 2) / 2;
|
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
|
||||||
|
|
||||||
scope_.resize(columns_);
|
scope_.resize(columns_);
|
||||||
|
|
||||||
if (rows_ != oldRows) {
|
if (rows_ != oldRows) {
|
||||||
barpixmap_ = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
|
barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||||
|
|
||||||
for (uint i = 0; i < FADE_SIZE; ++i)
|
for (uint i = 0; i < kFadeSize; ++i)
|
||||||
fade_bars_[i] = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
|
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||||
|
|
||||||
yscale_.resize(rows_ + 1);
|
yscale_.resize(rows_ + 1);
|
||||||
|
|
||||||
|
@ -113,13 +130,11 @@ void BlockAnalyzer::transform(Analyzer::Scope &s) {
|
||||||
|
|
||||||
for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
|
for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
|
||||||
|
|
||||||
float* front = static_cast<float*>(&s.front());
|
fht_->spectrum(s.data());
|
||||||
|
fht_->scale(s.data(), 1.0 / 20);
|
||||||
fht_->spectrum(front);
|
|
||||||
fht_->scale(front, 1.0 / 20);
|
|
||||||
|
|
||||||
// the second half is pretty dull, so only show it if the user has a large analyzer by setting to scope_.size() if large we prevent interpolation of large analyzers, this is good!
|
// the second half is pretty dull, so only show it if the user has a large analyzer by setting to scope_.size() if large we prevent interpolation of large analyzers, this is good!
|
||||||
s.resize(scope_.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : scope_.size());
|
s.resize(scope_.size() <= kMaxColumns / 2 ? kMaxColumns / 2 : scope_.size());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,35 +169,33 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
|
||||||
// determine y
|
// determine y
|
||||||
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
||||||
|
|
||||||
// this is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||||
if ((float)y > store_[x])
|
if (static_cast<float>(y) > store_[x])
|
||||||
y = int(store_[x] += step_);
|
y = static_cast<int>(store_[x] += step_);
|
||||||
else
|
else
|
||||||
store_[x] = y;
|
store_[x] = y;
|
||||||
|
|
||||||
// if y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
|
// If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
|
||||||
// if the fadeout is quite faded now, then display the new one
|
// if the fadeout is quite faded now, then display the new one
|
||||||
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < FADE_SIZE / 3*/) {
|
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
|
||||||
fade_pos_[x] = y;
|
fade_pos_[x] = y;
|
||||||
fade_intensity_[x] = FADE_SIZE;
|
fade_intensity_[x] = kFadeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fade_intensity_[x] > 0) {
|
if (fade_intensity_[x] > 0) {
|
||||||
const uint offset = --fade_intensity_[x];
|
const uint offset = --fade_intensity_[x];
|
||||||
const uint y = y_ + (fade_pos_[x] * (HEIGHT + 1));
|
const uint y = y_ + (fade_pos_[x] * (kHeight + 1));
|
||||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y, fade_bars_[offset], 0, 0, WIDTH, height() - y);
|
canvas_painter.drawPixmap(x * (kWidth + 1), y, fade_bars_[offset], 0, 0, kWidth, height() - y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
|
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
|
||||||
|
|
||||||
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are
|
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are
|
||||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, *bar(),
|
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
|
||||||
0, y * (HEIGHT + 1), bar()->width(),
|
|
||||||
bar()->height());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint x = 0; x < store_.size(); ++x)
|
for (uint x = 0; x < store_.size(); ++x)
|
||||||
canvas_painter.drawPixmap(x * (WIDTH + 1), int(store_[x]) * (HEIGHT + 1) + y_, topbarpixmap_);
|
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
|
||||||
|
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
|
||||||
|
@ -223,11 +236,11 @@ static inline void adjustToLimits(int &b, int &f, uint &amount) {
|
||||||
* It won't modify the hue of fg unless absolutely necessary
|
* It won't modify the hue of fg unless absolutely necessary
|
||||||
* @return the adjusted form of fg
|
* @return the adjusted form of fg
|
||||||
*/
|
*/
|
||||||
QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount = 150) {
|
||||||
|
|
||||||
class OutputOnExit {
|
class OutputOnExit {
|
||||||
public:
|
public:
|
||||||
OutputOnExit(const QColor &color) : c(color) {}
|
explicit OutputOnExit(const QColor &color) : c(color) {}
|
||||||
~OutputOnExit() {
|
~OutputOnExit() {
|
||||||
int h, s, v;
|
int h, s, v;
|
||||||
c.getHsv(&h, &s, &v);
|
c.getHsv(&h, &s, &v);
|
||||||
|
@ -237,14 +250,6 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||||
const QColor &c;
|
const QColor &c;
|
||||||
};
|
};
|
||||||
|
|
||||||
// hack so I don't have to cast everywhere
|
|
||||||
#define amount static_cast<int>(_amount)
|
|
||||||
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
|
|
||||||
// #define STAMP1( string ) debug() << string << ": " <<
|
|
||||||
// (QValueList<int>() << fh << fs << fv) << endl;
|
|
||||||
// #define STAMP2( string, value ) debug() << string << "=" << value << ":
|
|
||||||
// " << (QValueList<int>() << fh << fs << fv) << endl;
|
|
||||||
|
|
||||||
OutputOnExit allocateOnTheStack(fg);
|
OutputOnExit allocateOnTheStack(fg);
|
||||||
|
|
||||||
int bh, bs, bv;
|
int bh, bs, bv;
|
||||||
|
@ -255,23 +260,17 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||||
|
|
||||||
int dv = abs(bv - fv);
|
int dv = abs(bv - fv);
|
||||||
|
|
||||||
// STAMP2( "DV", dv );
|
|
||||||
|
|
||||||
// value is the best measure of contrast
|
// value is the best measure of contrast
|
||||||
// if there is enough difference in value already, return fg unchanged
|
// if there is enough difference in value already, return fg unchanged
|
||||||
if (dv > amount) return fg;
|
if (dv > static_cast<int>(amount)) return fg;
|
||||||
|
|
||||||
int ds = abs(bs - fs);
|
int ds = abs(bs - fs);
|
||||||
|
|
||||||
// STAMP2( "DS", ds );
|
|
||||||
|
|
||||||
// saturation is good enough too. But not as good. TODO adapt this a little
|
// saturation is good enough too. But not as good. TODO adapt this a little
|
||||||
if (ds > amount) return fg;
|
if (ds > static_cast<int>(amount)) return fg;
|
||||||
|
|
||||||
int dh = abs(bh - fh);
|
int dh = abs(bh - fh);
|
||||||
|
|
||||||
// STAMP2( "DH", dh );
|
|
||||||
|
|
||||||
if (dh > 120) {
|
if (dh > 120) {
|
||||||
// a third of the colour wheel automatically guarentees contrast
|
// a third of the colour wheel automatically guarentees contrast
|
||||||
// but only if the values are high enough and saturations significant enough
|
// but only if the values are high enough and saturations significant enough
|
||||||
|
@ -279,79 +278,49 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||||
|
|
||||||
// check the saturation for the two colours is sufficient that hue alone can
|
// check the saturation for the two colours is sufficient that hue alone can
|
||||||
// provide sufficient contrast
|
// provide sufficient contrast
|
||||||
if (ds > amount / 2 && (bs > 125 && fs > 125))
|
if (ds > static_cast<int>(amount) / 2 && (bs > 125 && fs > 125))
|
||||||
// STAMP1( "Sufficient saturation difference, and hues are
|
|
||||||
// compliemtary" );
|
|
||||||
return fg;
|
return fg;
|
||||||
else if (dv > amount / 2 && (bv > 125 && fv > 125))
|
else if (dv > static_cast<int>(amount) / 2 && (bv > 125 && fv > 125))
|
||||||
// STAMP1( "Sufficient value difference, and hues are
|
|
||||||
// compliemtary" );
|
|
||||||
return fg;
|
return fg;
|
||||||
|
|
||||||
// STAMP1( "Hues are complimentary but we must modify the value or
|
|
||||||
// saturation of the contrasting colour" );
|
|
||||||
|
|
||||||
// but either the colours are two desaturated, or too dark
|
|
||||||
// so we need to adjust the system, although not as much
|
|
||||||
///_amount /= 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs < 50 && ds < 40) {
|
if (fs < 50 && ds < 40) {
|
||||||
// low saturation on a low saturation is sad
|
// low saturation on a low saturation is sad
|
||||||
const int tmp = 50 - fs;
|
const int tmp = 50 - fs;
|
||||||
fs = 50;
|
fs = 50;
|
||||||
if (amount > tmp)
|
if (static_cast<int>(amount) > tmp)
|
||||||
_amount -= tmp;
|
amount -= tmp;
|
||||||
else
|
else
|
||||||
_amount = 0;
|
amount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// test that there is available value to honor our contrast requirement
|
// test that there is available value to honor our contrast requirement
|
||||||
if (255 - dv < amount) {
|
if (255 - dv < static_cast<int>(amount)) {
|
||||||
// we have to modify the value and saturation of fg
|
// we have to modify the value and saturation of fg
|
||||||
// adjustToLimits( bv, fv, amount );
|
// adjustToLimits( bv, fv, amount );
|
||||||
|
|
||||||
// STAMP
|
|
||||||
|
|
||||||
// see if we need to adjust the saturation
|
// see if we need to adjust the saturation
|
||||||
if (amount > 0) adjustToLimits(bs, fs, _amount);
|
if (static_cast<int>(amount) > 0) adjustToLimits(bs, fs, amount);
|
||||||
|
|
||||||
// STAMP
|
|
||||||
|
|
||||||
// see if we need to adjust the hue
|
// see if we need to adjust the hue
|
||||||
if (amount > 0) fh += amount; // cycles around;
|
if (static_cast<int>(amount) > 0)
|
||||||
|
fh += static_cast<int>(amount); // cycles around;
|
||||||
// STAMP
|
|
||||||
|
|
||||||
return QColor::fromHsv(fh, fs, fv);
|
return QColor::fromHsv(fh, fs, fv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STAMP
|
if (fv > bv && bv > static_cast<int>(amount))
|
||||||
|
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
|
||||||
|
|
||||||
if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount);
|
if (fv < bv && fv > static_cast<int>(amount))
|
||||||
|
return QColor::fromHsv(fh, fs, fv - static_cast<int>(amount));
|
||||||
|
|
||||||
// STAMP
|
if (fv > bv && (255 - fv > static_cast<int>(amount)))
|
||||||
|
return QColor::fromHsv(fh, fs, fv + static_cast<int>(amount));
|
||||||
|
|
||||||
if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - amount);
|
if (fv < bv && (255 - bv > static_cast<int>(amount)))
|
||||||
|
return QColor::fromHsv(fh, fs, bv + static_cast<int>(amount));
|
||||||
// STAMP
|
|
||||||
|
|
||||||
if (fv > bv && (255 - fv > amount))
|
|
||||||
return QColor::fromHsv(fh, fs, fv + amount);
|
|
||||||
|
|
||||||
// STAMP
|
|
||||||
|
|
||||||
if (fv < bv && (255 - bv > amount))
|
|
||||||
return QColor::fromHsv(fh, fs, bv + amount);
|
|
||||||
|
|
||||||
// STAMP
|
|
||||||
// debug() << "Something went wrong!\n";
|
|
||||||
|
|
||||||
return Qt::blue;
|
return Qt::blue;
|
||||||
|
|
||||||
#undef amount
|
|
||||||
// #undef STAMP
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockAnalyzer::paletteChange(const QPalette&) {
|
void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||||
|
@ -361,17 +330,17 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||||
|
|
||||||
topbarpixmap_.fill(fg);
|
topbarpixmap_.fill(fg);
|
||||||
|
|
||||||
const double dr = 15 * double(bg.red() - fg.red()) / (rows_ * 16);
|
const double dr = 15 * static_cast<double>(bg.red() - fg.red()) / (rows_ * 16);
|
||||||
const double dg = 15 * double(bg.green() - fg.green()) / (rows_ * 16);
|
const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
|
||||||
const double db = 15 * double(bg.blue() - fg.blue()) / (rows_ * 16);
|
const double db = 15 * static_cast<double>(bg.blue() - fg.blue()) / (rows_ * 16);
|
||||||
const int r = fg.red(), g = fg.green(), b = fg.blue();
|
const int r = fg.red(), g = fg.green(), b = fg.blue();
|
||||||
|
|
||||||
bar()->fill(bg);
|
bar()->fill(bg);
|
||||||
|
|
||||||
QPainter p(bar());
|
QPainter p(bar());
|
||||||
for (int y = 0; (uint)y < rows_; ++y)
|
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
|
||||||
// graduate the fg color
|
// graduate the fg color
|
||||||
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * y), g + int(dg * y), b + int(db * y)));
|
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
|
||||||
|
|
||||||
{
|
{
|
||||||
const QColor bg = palette().color(QPalette::Background).dark(112);
|
const QColor bg = palette().color(QPalette::Background).dark(112);
|
||||||
|
@ -388,12 +357,12 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||||
const int r = bg.red(), g = bg.green(), b = bg.blue();
|
const int r = bg.red(), g = bg.green(), b = bg.blue();
|
||||||
|
|
||||||
// Precalculate all fade-bar pixmaps
|
// Precalculate all fade-bar pixmaps
|
||||||
for (uint y = 0; y < FADE_SIZE; ++y) {
|
for (uint y = 0; y < kFadeSize; ++y) {
|
||||||
fade_bars_[y].fill(palette().color(QPalette::Background));
|
fade_bars_[y].fill(palette().color(QPalette::Background));
|
||||||
QPainter f(&fade_bars_[y]);
|
QPainter f(&fade_bars_[y]);
|
||||||
for (int z = 0; (uint)z < rows_; ++z) {
|
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
|
||||||
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
|
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
|
||||||
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * Y), g + int(dg * Y), b + int(db * Y)));
|
f.fillRect(0, z * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y), b + static_cast<int>(db * Y)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,14 +373,21 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||||
|
|
||||||
void BlockAnalyzer::drawBackground() {
|
void BlockAnalyzer::drawBackground() {
|
||||||
|
|
||||||
|
if (background_.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const QColor bg = palette().color(QPalette::Background);
|
const QColor bg = palette().color(QPalette::Background);
|
||||||
const QColor bgdark = bg.dark(112);
|
const QColor bgdark = bg.dark(112);
|
||||||
|
|
||||||
background_.fill(bg);
|
background_.fill(bg);
|
||||||
|
|
||||||
QPainter p(&background_);
|
QPainter p(&background_);
|
||||||
|
|
||||||
|
if (p.paintEngine() == 0) return;
|
||||||
|
|
||||||
for (int x = 0; (uint)x < columns_; ++x)
|
for (int x = 0; (uint)x < columns_; ++x)
|
||||||
for (int y = 0; (uint)y < rows_; ++y)
|
for (int y = 0; (uint)y < rows_; ++y)
|
||||||
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, WIDTH, HEIGHT, bgdark);
|
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
// Maintainer: Max Howell <mac.howell@methylblue.com>, (C) 2003-5
|
/*
|
||||||
// Copyright: See COPYING file that comes with this distribution
|
Strawberry Music Player
|
||||||
//
|
This file was part of Amarok.
|
||||||
|
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||||
|
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef BLOCKANALYZER_H
|
#ifndef BLOCKANALYZER_H
|
||||||
#define BLOCKANALYZER_H
|
#define BLOCKANALYZER_H
|
||||||
|
@ -11,6 +30,7 @@
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QVector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -21,22 +41,18 @@
|
||||||
|
|
||||||
class QResizeEvent;
|
class QResizeEvent;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Max Howell
|
|
||||||
*/
|
|
||||||
|
|
||||||
class BlockAnalyzer : public Analyzer::Base {
|
class BlockAnalyzer : public Analyzer::Base {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
||||||
~BlockAnalyzer();
|
~BlockAnalyzer();
|
||||||
|
|
||||||
static const uint HEIGHT;
|
static const uint kHeight;
|
||||||
static const uint WIDTH;
|
static const uint kWidth;
|
||||||
static const uint MIN_ROWS;
|
static const uint kMinRows;
|
||||||
static const uint MIN_COLUMNS;
|
static const uint kMinColumns;
|
||||||
static const uint MAX_COLUMNS;
|
static const uint kMaxColumns;
|
||||||
static const uint FADE_SIZE;
|
static const uint kFadeSize;
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
|
@ -53,22 +69,21 @@ class BlockAnalyzer : public Analyzer::Base {
|
||||||
private:
|
private:
|
||||||
QPixmap *bar() { return &barpixmap_; }
|
QPixmap *bar() { return &barpixmap_; }
|
||||||
|
|
||||||
uint columns_, rows_; // number of rows and columns of blocks
|
uint columns_, rows_; // number of rows and columns of blocks
|
||||||
uint y_; // y-offset from top of widget
|
uint y_; // y-offset from top of widget
|
||||||
QPixmap barpixmap_;
|
QPixmap barpixmap_;
|
||||||
QPixmap topbarpixmap_;
|
QPixmap topbarpixmap_;
|
||||||
QPixmap background_;
|
QPixmap background_;
|
||||||
QPixmap canvas_;
|
QPixmap canvas_;
|
||||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||||
std::vector<float> store_; // current bar heights
|
QVector<float> store_; // current bar heights
|
||||||
std::vector<float> yscale_;
|
QVector<float> yscale_;
|
||||||
|
|
||||||
// FIXME why can't I namespace these? c++ issue?
|
QVector<QPixmap> fade_bars_;
|
||||||
std::vector<QPixmap> fade_bars_;
|
QVector<uint> fade_pos_;
|
||||||
std::vector<uint> fade_pos_;
|
QVector<int> fade_intensity_;
|
||||||
std::vector<int> fade_intensity_;
|
|
||||||
|
|
||||||
float step_; // rows to fall per frame
|
float step_; // rows to fall per frame
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // BLOCKANALYZER_H
|
||||||
|
|
|
@ -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_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
/*
|
||||||
//
|
Strawberry Music Player
|
||||||
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
|
This file was part of Clementine.
|
||||||
//
|
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
|
||||||
// This program is free software; you can redistribute it and/or
|
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
// modify it under the terms of the GNU General Public License as
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
Copyright 2017, Santiago Gil
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
#include <math.h>
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
#include <string.h>
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
|
|
||||||
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
|
#include <algorithm>
|
||||||
if (n < 3) {
|
#include <cmath>
|
||||||
m_num = 0;
|
|
||||||
m_exp2 = -1;
|
#include <QVector>
|
||||||
return;
|
|
||||||
}
|
FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
|
||||||
m_exp2 = n;
|
|
||||||
m_num = 1 << n;
|
|
||||||
if (n > 3) {
|
if (n > 3) {
|
||||||
m_buf = new float[m_num];
|
buf_vector_.resize(num_);
|
||||||
m_tab = new float[m_num * 2];
|
tab_vector_.resize(num_ * 2);
|
||||||
makeCasTable();
|
makeCasTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FHT::~FHT() {
|
FHT::~FHT() {}
|
||||||
delete[] m_buf;
|
|
||||||
delete[] m_tab;
|
int FHT::sizeExp() const { return exp2_; }
|
||||||
delete[] m_log;
|
int FHT::size() const { return num_; }
|
||||||
}
|
|
||||||
|
float* FHT::buf_() { return buf_vector_.data(); }
|
||||||
|
float* FHT::tab_() { return tab_vector_.data(); }
|
||||||
|
int* FHT::log_() { return log_vector_.data(); }
|
||||||
|
|
||||||
void FHT::makeCasTable(void) {
|
void FHT::makeCasTable(void) {
|
||||||
float d, *costab, *sintab;
|
float* costab = tab_();
|
||||||
int ul, ndiv2 = m_num / 2;
|
float* sintab = tab_() + num_ / 2 + 1;
|
||||||
|
|
||||||
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
|
for (int ul = 0; ul < num_; ul++) {
|
||||||
d = M_PI * ul / ndiv2;
|
float d = M_PI * ul / (num_ / 2);
|
||||||
*costab = *sintab = cos(d);
|
*costab = *sintab = cos(d);
|
||||||
|
|
||||||
costab += 2, sintab += 2;
|
costab += 2;
|
||||||
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
|
sintab += 2;
|
||||||
|
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float* FHT::copy(float* d, float* s) {
|
|
||||||
return (float*)memcpy(d, s, m_num * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
float* FHT::clear(float* d) {
|
|
||||||
return (float*)memset(d, 0, m_num * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FHT::scale(float* p, float d) {
|
void FHT::scale(float* p, float d) {
|
||||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
|
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::ewma(float* d, float* s, float w) {
|
void FHT::ewma(float* d, float* s, float w) {
|
||||||
for (int i = 0; i < (m_num / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::logSpectrum(float* out, float* p) {
|
void FHT::logSpectrum(float* out, float* p) {
|
||||||
int n = m_num / 2, i, j, k, *r;
|
|
||||||
if (!m_log) {
|
int n = num_ / 2, i, j, k, *r;
|
||||||
m_log = new int[n];
|
if (log_vector_.size() < n) {
|
||||||
float f = n / log10((double)n);
|
log_vector_.resize(n);
|
||||||
for (i = 0, r = m_log; i < n; i++, r++) {
|
float f = n / log10(static_cast<double>(n));
|
||||||
j = int(rint(log10(i + 1.0) * f));
|
for (i = 0, r = log_(); i < n; i++, r++) {
|
||||||
|
j = static_cast<int>(rint(log10(i + 1.0) * f));
|
||||||
*r = j >= n ? n - 1 : j;
|
*r = j >= n ? n - 1 : j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
semiLogSpectrum(p);
|
semiLogSpectrum(p);
|
||||||
*out++ = *p = *p / 100;
|
*out++ = *p = *p / 100;
|
||||||
for (k = i = 1, r = m_log; i < n; i++) {
|
for (k = i = 1, r = log_(); i < n; i++) {
|
||||||
j = *r++;
|
j = *r++;
|
||||||
if (i == j)
|
if (i == j) {
|
||||||
*out++ = p[i];
|
*out++ = p[i];
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
float base = p[k - 1];
|
float base = p[k - 1];
|
||||||
float step = (p[j] - base) / (j - (k - 1));
|
float step = (p[j] - base) / (j - (k - 1));
|
||||||
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
|
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::semiLogSpectrum(float* p) {
|
void FHT::semiLogSpectrum(float* p) {
|
||||||
float e;
|
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++, p++) {
|
for (int i = 0; i < (num_ / 2); i++, p++) {
|
||||||
e = 10.0 * log10(sqrt(*p * .5));
|
float e = 10.0 * log10(sqrt(*p / 2));
|
||||||
*p = e < 0 ? 0 : e;
|
*p = e < 0 ? 0 : e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::spectrum(float* p) {
|
void FHT::spectrum(float* p) {
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++, p++) *p = (float)sqrt(*p * .5);
|
for (int i = 0; i < (num_ / 2); i++, p++)
|
||||||
|
*p = static_cast<float>(sqrt(*p / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::power(float* p) {
|
void FHT::power(float* p) {
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
|
for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::power2(float* p) {
|
void FHT::power2(float* p) {
|
||||||
int i;
|
_transform(p, num_, 0);
|
||||||
float* q;
|
|
||||||
_transform(p, m_num, 0);
|
|
||||||
|
|
||||||
*p = (*p * *p), *p += *p, p++;
|
*p = static_cast<float>(2 * pow(*p, 2));
|
||||||
|
p++;
|
||||||
|
|
||||||
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
|
float* q = p + num_ - 2;
|
||||||
*p = (*p * *p) + (*q * *q), p++;
|
for (int i = 1; i < (num_ / 2); i++) {
|
||||||
|
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
|
||||||
|
p++;
|
||||||
|
q--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::transform(float* p) {
|
void FHT::transform(float* p) {
|
||||||
if (m_num == 8)
|
if (num_ == 8)
|
||||||
transform8(p);
|
transform8(p);
|
||||||
else
|
else
|
||||||
_transform(p, m_num, 0);
|
_transform(p, num_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::transform8(float* p) {
|
void FHT::transform8(float* p) {
|
||||||
|
|
||||||
float a, b, c, d, e, f, g, h, b_f2, d_h2;
|
float a, b, c, d, e, f, g, h, b_f2, d_h2;
|
||||||
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
|
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
|
||||||
|
|
||||||
|
@ -159,6 +159,7 @@ void FHT::transform8(float* p) {
|
||||||
*--p = a_ce_g + b_df_h;
|
*--p = a_ce_g + b_df_h;
|
||||||
*--p = ac_e_g + b_f2;
|
*--p = ac_e_g + b_f2;
|
||||||
*--p = aceg + bdfh;
|
*--p = aceg + bdfh;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FHT::_transform(float* p, int n, int k) {
|
void FHT::_transform(float* p, int n, int k) {
|
||||||
|
@ -171,19 +172,19 @@ void FHT::_transform(float* p, int n, int k) {
|
||||||
int i, j, ndiv2 = n / 2;
|
int i, j, ndiv2 = n / 2;
|
||||||
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
|
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
|
||||||
|
|
||||||
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
for (i = 0, t1 = buf_(), t2 = buf_() + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
||||||
*t1++ = *pp++, *t2++ = *pp++;
|
*t1++ = *pp++, *t2++ = *pp++;
|
||||||
|
|
||||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
std::copy(buf_(), buf_() + n, p + k);
|
||||||
|
|
||||||
_transform(p, ndiv2, k);
|
_transform(p, ndiv2, k);
|
||||||
_transform(p, ndiv2, k + ndiv2);
|
_transform(p, ndiv2, k + ndiv2);
|
||||||
|
|
||||||
j = m_num / ndiv2 - 1;
|
j = num_ / ndiv2 - 1;
|
||||||
t1 = m_buf;
|
t1 = buf_();
|
||||||
t2 = t1 + ndiv2;
|
t2 = t1 + ndiv2;
|
||||||
t3 = p + k + ndiv2;
|
t3 = p + k + ndiv2;
|
||||||
ptab = m_tab;
|
ptab = tab_();
|
||||||
pp = p + k;
|
pp = p + k;
|
||||||
|
|
||||||
a = *ptab++ * *t3++;
|
a = *ptab++ * *t3++;
|
||||||
|
@ -200,5 +201,7 @@ void FHT::_transform(float* p, int n, int k) {
|
||||||
*t1++ = *pp + a;
|
*t1++ = *pp + a;
|
||||||
*t2++ = *pp++ - a;
|
*t2++ = *pp++ - a;
|
||||||
}
|
}
|
||||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
|
||||||
|
std::copy(buf_(), buf_() + n, p + k);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
// FHT - Fast Hartley Transform Class
|
/*
|
||||||
//
|
Strawberry Music Player
|
||||||
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
|
This file was part of Clementine.
|
||||||
//
|
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
|
||||||
// This program is free software; you can redistribute it and/or
|
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||||
// modify it under the terms of the GNU General Public License as
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
Copyright 2017, Santiago Gil
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
// This program is distributed in the hope that it will be useful, but
|
it under the terms of the GNU General Public License as published by
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
(at your option) any later version.
|
||||||
// General Public License for more details.
|
|
||||||
//
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
// You should have received a copy of the GNU General Public License
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// along with this program; if not, write to the Free Software
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA
|
GNU General Public License for more details.
|
||||||
//
|
|
||||||
// $Id$
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef FHT_H
|
#ifndef FHT_H
|
||||||
#define FHT_H
|
#define FHT_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the Hartley Transform after Bracewell's discrete
|
* Implementation of the Hartley Transform after Bracewell's discrete
|
||||||
* algorithm. The algorithm is subject to US patent No. 4,646,256 (1987)
|
* algorithm. The algorithm is subject to US patent No. 4,646,256 (1987)
|
||||||
|
@ -30,11 +34,16 @@
|
||||||
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
||||||
*/
|
*/
|
||||||
class FHT {
|
class FHT {
|
||||||
int m_exp2;
|
const int num_;
|
||||||
int m_num;
|
const int exp2_;
|
||||||
float* m_buf;
|
|
||||||
float* m_tab;
|
QVector<float> buf_vector_;
|
||||||
int* m_log;
|
QVector<float> tab_vector_;
|
||||||
|
QVector<int> log_vector_;
|
||||||
|
|
||||||
|
float* buf_();
|
||||||
|
float* tab_();
|
||||||
|
int* log_();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a table of "cas" (cosine and sine) values.
|
* Create a table of "cas" (cosine and sine) values.
|
||||||
|
@ -57,10 +66,8 @@ class FHT {
|
||||||
FHT(int);
|
FHT(int);
|
||||||
|
|
||||||
~FHT();
|
~FHT();
|
||||||
inline int sizeExp() const { return m_exp2; }
|
int sizeExp() const;
|
||||||
inline int size() const { return m_num; }
|
int size() const;
|
||||||
float* copy(float*, float*);
|
|
||||||
float* clear(float*);
|
|
||||||
void scale(float*, float);
|
void scale(float*, float);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,4 +122,4 @@ class FHT {
|
||||||
void transform(float*);
|
void transform(float*);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // FHT_H
|
||||||
|
|
|
@ -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) {}
|
|
@ -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…
Reference in New Issue