Add boom and rainbow analyzers

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

View File

@ -29,6 +29,8 @@
<file>pictures/osd_shadow_corner.png</file>
<file>pictures/osd_shadow_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

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

34
dist/debian/copyright vendored
View File

@ -59,8 +59,8 @@ Files: src/core/main.h
src/scrobbler/*
src/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

View File

@ -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

View File

@ -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;
}

View File

@ -1,5 +1,24 @@
// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004
// Copyright: See COPYING file that comes with this distribution
/*
Strawberry Music Player
This file was part of Amarok.
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
Copyright 2009-2012, David Sansome <me@davidsansome.com>
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2017, Santiago Gil
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANALYZERBASE_H
#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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,6 +1,25 @@
// Maintainer: Max Howell <mac.howell@methylblue.com>, (C) 2003-5
// Copyright: See COPYING file that comes with this distribution
//
/*
Strawberry Music Player
This file was part of Amarok.
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
Copyright 2005, Mark Kretschmann <markey@web.de>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BLOCKANALYZER_H
#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

View File

@ -0,0 +1,169 @@
/*
Strawberry Music Player
This file was part of Clementine.
Copyright 2004, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boomanalyzer.h"
#include <cmath>
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QColor>
using Analyzer::Scope;
const uint BoomAnalyzer::kColumnWidth = 4;
const uint BoomAnalyzer::kMaxBandCount = 256;
const uint BoomAnalyzer::kMinBandCount = 32;
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
bands_(0),
scope_(kMinBandCount),
fg_(palette().color(QPalette::Highlight)),
K_barHeight_(1.271) // 1.471
,
F_peakSpeed_(1.103) // 1.122
,
F_(1.0),
bar_height_(kMaxBandCount, 0),
peak_height_(kMaxBandCount, 0),
peak_speed_(kMaxBandCount, 0.01),
barPixmap_(kColumnWidth, 50) {
setMinimumWidth(kMinBandCount * (kColumnWidth + 1) - 1);
setMaximumWidth(kMaxBandCount * (kColumnWidth + 1) - 1);
}
void BoomAnalyzer::changeK_barHeight(int newValue) {
K_barHeight_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
bands_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_);
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
QPainter p(&barPixmap_);
for (uint y = 0; y < HEIGHT; ++y) {
const double F = static_cast<double>(y) * h;
p.setPen(QColor(qMax(0, 255 - static_cast<int>(229.0 * F)),
qMax(0, 255 - static_cast<int>(229.0 * F)),
qMax(0, 255 - static_cast<int>(191.0 * F))));
p.drawLine(0, y, kColumnWidth - 2, y);
}
}
void BoomAnalyzer::transform(Scope& s) {
fht_->spectrum(s.data());
fht_->scale(s.data(), 1.0 / 50);
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2 : scope_.size());
}
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
if (!new_frame || engine_->state() == Engine::Paused) {
p.drawPixmap(0, 0, canvas_);
return;
}
float h;
const uint MAX_HEIGHT = height() - 1;
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Background));
Analyzer::interpolate(scope, scope_);
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
h = log10(scope_[i] * 256.0) * F_;
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
if (h > bar_height_[i]) {
bar_height_[i] = h;
if (h > peak_height_[i]) {
peak_height_[i] = h;
peak_speed_[i] = 0.01;
}
else {
goto peak_handling;
}
}
else {
if (bar_height_[i] > 0.0) {
bar_height_[i] -= K_barHeight_; // 1.4
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
}
peak_handling:
if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i];
peak_speed_[i] *= F_peakSpeed_; // 1.12
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
}
}
y = height() - uint(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0)
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
y = height() - uint(peak_height_[i]);
canvas_painter.setPen(palette().color(QPalette::Midlight));
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
}
p.drawPixmap(0, 0, canvas_);
}

View File

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

View File

@ -1,140 +1,140 @@
// FHT - Fast Hartley Transform Class
//
// 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);
}

View File

@ -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

View File

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

View File

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