Add boom and rainbow analyzers
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user