Move everything to trunk

This commit is contained in:
David Sansome 2009-12-24 19:16:07 +00:00
commit 5b0496bf8f
104 changed files with 10533 additions and 0 deletions

13
TODO Normal file
View File

@ -0,0 +1,13 @@
- Crossfading
- Drag & drop from files tab
- Double click folders in files tab
- Nice error messages
- Automatically install xine plugins
Long-term:
- Last.fm
- iPod
Windows:
- Playlist delegates
- Document build steps

BIN
data/album.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
data/artist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

BIN
data/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

BIN
data/configure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

BIN
data/currenttrack_pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

BIN
data/currenttrack_play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

35
data/data.qrc Normal file
View File

@ -0,0 +1,35 @@
<RCC>
<qresource prefix="/">
<file>clear.png</file>
<file>go-home.png</file>
<file>go-next.png</file>
<file>go-previous.png</file>
<file>go-up.png</file>
<file>media-playback-pause.png</file>
<file>media-playback-start.png</file>
<file>media-playback-stop.png</file>
<file>media-skip-backward.png</file>
<file>media-skip-forward.png</file>
<file>mainwindow.css</file>
<file>schema.sql</file>
<file>volumeslider-handle_glow.png</file>
<file>volumeslider-handle.png</file>
<file>volumeslider-inset.png</file>
<file>volumeslider-gradient.png</file>
<file>logo.png</file>
<file>icon.png</file>
<file>currenttrack_bar_left.png</file>
<file>currenttrack_bar_mid.png</file>
<file>currenttrack_bar_right.png</file>
<file>currenttrack_pause.png</file>
<file>currenttrack_play.png</file>
<file>album.png</file>
<file>artist.png</file>
<file>files.png</file>
<file>nomusic.png</file>
<file>folder-new.png</file>
<file>folder.png</file>
<file>configure.png</file>
<file>exit.png</file>
</qresource>
</RCC>

BIN
data/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
data/files.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

BIN
data/folder-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

BIN
data/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

BIN
data/go-home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

BIN
data/go-next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

BIN
data/go-previous.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
data/go-up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

BIN
data/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
data/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
data/logo.xcf Normal file

Binary file not shown.

8
data/mainwindow.css Normal file
View File

@ -0,0 +1,8 @@
#playlist {
background-color: white;
background-image: url(:logo.png);
background-attachment: fixed;
background-position: bottom right;
background-repeat: none;
background-clip: content;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

BIN
data/media-skip-forward.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

BIN
data/nomusic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

32
data/schema.sql Normal file
View File

@ -0,0 +1,32 @@
CREATE TABLE directories (
path TEXT NOT NULL,
subdirs INTEGER NOT NULL
);
CREATE TABLE songs (
/* Metadata from taglib */
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
composer TEXT,
track INTEGER,
disc INTEGER,
bpm REAL,
year INTEGER,
genre TEXT,
comment TEXT,
compilation INTEGER,
length INTEGER,
bitrate INTEGER,
samplerate INTEGER,
/* Information about the file on disk */
directory INTEGER NOT NULL,
filename TEXT NOT NULL,
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL,
filesize INTEGER NOT NULL
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

BIN
data/volumeslider-inset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

View File

@ -0,0 +1,219 @@
/***************************************************************************
viswidget.cpp - description
-------------------
begin : Die Jan 7 2003
copyright : (C) 2003 by Max Howell
email : markey@web.de
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "analyzerbase.h"
#include "enginebase.h"
#include <cmath> //interpolate()
#include <QEvent> //event()
#include <QPainter>
#include <QPaintEvent>
#include <QtDebug>
// INSTRUCTIONS Base2D
// 1. do anything that depends on height() in init(), Base2D will call it before you are shown
// 2. otherwise you can use the constructor to initialise things
// 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
//can't mod scope in analyze you have to use transform
//TODO 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 ) ) && !defined(Q_OS_WIN32)
template class Analyzer::Base<QWidget>;
#endif
Analyzer::Base::Base( QWidget *parent, uint timeout, uint scopeSize )
: QWidget( parent )
, m_timeout( timeout )
, m_fht( new FHT(scopeSize) )
, m_engine(NULL)
, m_lastScope(512)
{
connect( &m_timer, SIGNAL( timeout() ), SLOT( update() ) );
}
void Analyzer::Base::hideEvent(QHideEvent *) {
m_timer.stop();
}
void Analyzer::Base::showEvent(QShowEvent *) {
m_timer.start(timeout());
}
void Analyzer::Base::transform( Scope &scope ) //virtual
{
//this is a standard transformation that should give
//an FFT scope that has bands for pretty analyzers
//NOTE resizing here is redundant as FHT routines only calculate FHT::size() values
//scope.resize( m_fht->size() );
float *front = static_cast<float*>( &scope.front() );
float* f = new float[ m_fht->size() ];
m_fht->copy( &f[0], front );
m_fht->logSpectrum( front, &f[0] );
m_fht->scale( front, 1.0 / 20 );
scope.resize( m_fht->size() / 2 ); //second half of values are rubbish
delete [] f;
}
void Analyzer::Base::paintEvent(QPaintEvent * e)
{
EngineBase *engine = m_engine;
QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window));
switch( engine->state() )
{
case Engine::Playing:
{
const Engine::Scope &thescope = engine->scope();
int i = 0;
// convert to mono here - our built in analyzers need mono, but we the engines provide interleaved pcm
for( uint x = 0; (int)x < m_fht->size(); ++x )
{
m_lastScope[x] = double(thescope[i] + thescope[i+1]) / (2*(1<<15));
i += 2;
}
transform( m_lastScope );
analyze( p, m_lastScope );
//scope.resize( m_fht->size() );
break;
}
case Engine::Paused:
analyze(p, m_lastScope);
break;
default:
demo(p);
}
}
int Analyzer::Base::resizeExponent( int exp )
{
if ( exp < 3 )
exp = 3;
else if ( exp > 9 )
exp = 9;
if ( exp != m_fht->sizeExp() ) {
delete m_fht;
m_fht = new FHT( exp );
}
return exp;
}
int Analyzer::Base::resizeForBands( int bands )
{
int exp;
if ( bands <= 8 )
exp = 4;
else if ( bands <= 16 )
exp = 5;
else if ( bands <= 32 )
exp = 6;
else if ( bands <= 64 )
exp = 7;
else if ( bands <= 128 )
exp = 8;
else
exp = 9;
resizeExponent( exp );
return m_fht->size() / 2;
}
void Analyzer::Base::paused(QPainter& p) //virtual
{}
void Analyzer::Base::demo(QPainter& p) //virtual
{
static int t = 201; //FIXME make static to namespace perhaps
if( t > 999 ) t = 1; //0 = wasted calculations
if( t < 201 )
{
Scope s( 32 );
const double dt = double(t) / 200;
for( uint i = 0; i < s.size(); ++i )
s[i] = dt * (sin( M_PI + (i * M_PI) / s.size() ) + 1.0);
analyze( p, s );
}
else analyze( p, Scope( 32, 0 ) );
++t;
}
void Analyzer::Base::polishEvent()
{
init(); //virtual
}
void
Analyzer::interpolate( const Scope &inVec, Scope &outVec ) //static
{
double pos = 0.0;
const double step = (double)inVec.size() / outVec.size();
for ( uint i = 0; i < outVec.size(); ++i, pos += step )
{
const double error = pos - std::floor( pos );
const unsigned long offset = (unsigned long)pos;
unsigned long indexLeft = offset + 0;
if ( indexLeft >= inVec.size() )
indexLeft = inVec.size() - 1;
unsigned long indexRight = offset + 1;
if ( indexRight >= inVec.size() )
indexRight = inVec.size() - 1;
outVec[i] = inVec[indexLeft ] * ( 1.0 - error ) +
inVec[indexRight] * error;
}
}
void
Analyzer::initSin( Scope &v, const uint size ) //static
{
double step = ( M_PI * 2 ) / size;
double radian = 0;
for ( uint i = 0; i < size; i++ )
{
v.push_back( sin( radian ) );
radian += step;
}
}

View File

@ -0,0 +1,84 @@
// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004
// Copyright: See COPYING file that comes with this distribution
#ifndef ANALYZERBASE_H
#define ANALYZERBASE_H
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#include "engine_fwd.h"
#include "fht.h" //stack allocated and convenience
#include <QPixmap> //stack allocated and convenience
#include <QTimer> //stack allocated
#include <QWidget> //baseclass
#include <vector> //included for convenience
#include <QGLWidget> //baseclass
#ifdef Q_WS_MACX
#include <OpenGL/gl.h> //included for convenience
#include <OpenGL/glu.h> //included for convenience
#else
#include <GL/gl.h> //included for convenience
#include <GL/glu.h> //included for convenience
#endif
class QEvent;
class QPaintEvent;
class QResizeEvent;
namespace Analyzer {
typedef std::vector<float> Scope;
class Base : public QWidget
{
public:
uint timeout() const { return m_timeout; }
void set_engine(EngineBase* engine) { m_engine = engine; }
protected:
Base( QWidget*, uint timeout, uint scopeSize = 7 );
~Base() { delete m_fht; }
void hideEvent(QHideEvent *);
void showEvent(QShowEvent *);
void paintEvent( QPaintEvent* );
void polishEvent();
int resizeExponent( int );
int resizeForBands( int );
virtual void init() {}
virtual void transform( Scope& );
virtual void analyze( QPainter& p, const Scope& ) = 0;
virtual void paused(QPainter& p);
virtual void demo(QPainter& p);
void changeTimeout( uint newTimeout )
{
m_timer.setInterval( newTimeout );
m_timeout = newTimeout;
}
protected:
QTimer m_timer;
uint m_timeout;
FHT *m_fht;
EngineBase* m_engine;
Scope m_lastScope;
};
void interpolate( const Scope&, Scope& );
void initSin( Scope&, const uint = 6000 );
} //END namespace Analyzer
using Analyzer::Scope;
#endif

View File

@ -0,0 +1,127 @@
/***************************************************************************
analyzerfactory.cpp - description
-------------------
begin : Fre Nov 15 2002
copyright : (C) 2002 by Mark Kretschmann
email : markey@web.de
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include <config.h> //for HAVE_QGLWIDGET macro
#include "amarokcore/amarokconfig.h"
#include "analyzerbase.h" //declaration here
#include "baranalyzer.h"
#include "boomanalyzer.h"
#include "sonogram.h"
#include "turbine.h"
#include "blockanalyzer.h"
#ifdef HAVE_QGLWIDGET
#include "glanalyzer.h"
#include "glanalyzer2.h"
#include "glanalyzer3.h"
#endif
#include <qlabel.h>
#include <klocale.h>
//separate from analyzerbase.cpp to save compile time
QWidget *Analyzer::Factory::createAnalyzer( QWidget *parent )
{
//new XmmsWrapper(); //toplevel
QWidget *analyzer = 0;
switch( AmarokConfig::currentAnalyzer() )
{
case 2:
analyzer = new Sonogram( parent );
break;
case 1:
analyzer = new TurbineAnalyzer( parent );
break;
case 3:
analyzer = new BarAnalyzer( parent );
break;
case 4:
analyzer = new BlockAnalyzer( parent );
break;
#ifdef HAVE_QGLWIDGET
case 5:
analyzer = new GLAnalyzer( parent );
break;
case 6:
analyzer = new GLAnalyzer2( parent );
break;
case 7:
analyzer = new GLAnalyzer3( parent );
break;
case 8:
#else
case 5:
#endif
analyzer = new QLabel( i18n( "Click for Analyzers" ), parent ); //blank analyzer to satisfy Grue
static_cast<QLabel *>(analyzer)->setAlignment( Qt::AlignCenter );
break;
default:
AmarokConfig::setCurrentAnalyzer( 0 );
case 0:
analyzer = new BoomAnalyzer( parent );
}
return analyzer;
}
QWidget *Analyzer::Factory::createPlaylistAnalyzer( QWidget *parent)
{
QWidget *analyzer = 0;
switch( AmarokConfig::currentPlaylistAnalyzer() )
{
case 1:
analyzer = new TurbineAnalyzer( parent );
break;
case 2:
analyzer = new Sonogram( parent );
break;
case 3:
analyzer = new BoomAnalyzer( parent );
break;
#ifdef HAVE_QGLWIDGET
case 4:
analyzer = new GLAnalyzer( parent );
break;
case 5:
analyzer = new GLAnalyzer2( parent );
break;
case 6:
analyzer = new GLAnalyzer3( parent );
break;
case 7:
#else
case 4:
#endif
analyzer = new QLabel( i18n( "Click for Analyzers" ), parent ); //blank analyzer to satisfy Grue
static_cast<QLabel *>(analyzer)->setAlignment( Qt::AlignCenter );
break;
default:
AmarokConfig::setCurrentPlaylistAnalyzer( 0 );
case 0:
analyzer = new BlockAnalyzer( parent );
break;
}
return analyzer;
}

View File

@ -0,0 +1,167 @@
//
//
// C++ Implementation: $MODULE$
//
// Description:
//
//
// Author: Mark Kretschmann <markey@web.de>, (C) 2003
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "baranalyzer.h"
#include <cmath> //log10(), etc.
#include <QtDebug>
#include <QPainter>
BarAnalyzer::BarAnalyzer( QWidget *parent )
: Analyzer::Base( parent, 12, 8 )
//, m_bands( BAND_COUNT )
//, barVector( BAND_COUNT, 0 )
//, roofVector( BAND_COUNT, 50 )
//, roofVelocityVector( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR )
{
//roof pixmaps don't depend on size() so we do in the ctor
m_bg = parent->palette().color(QPalette::Background);
QColor fg( 0xff, 0x50, 0x70 );
double dr = double(m_bg.red() - fg.red()) / (NUM_ROOFS-1); //-1 because we start loop below at 0
double dg = double(m_bg.green() - fg.green()) / (NUM_ROOFS-1);
double db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS-1);
for ( uint i = 0; i < NUM_ROOFS; ++i )
{
m_pixRoof[i] = QPixmap( COLUMN_WIDTH, 1 );
m_pixRoof[i].fill( QColor( fg.red()+int(dr*i), fg.green()+int(dg*i), fg.blue()+int(db*i) ) );
}
}
void BarAnalyzer::resizeEvent( QResizeEvent * e )
{
qDebug() << "Baranalyzer Resized(" << width() << "x" << height() << ")";
init();
}
// METHODS =====================================================
void BarAnalyzer::init()
{
const double MAX_AMPLITUDE = 1.0;
const double F = double(height() - 2) / (log10( 255 ) * MAX_AMPLITUDE );
BAND_COUNT = width() / 5;
MAX_DOWN = int(0 -(qMax(1, height() / 50)));
MAX_UP = int(qMax(1, height() / 25));
qDebug() << "BAND_COUNT = " << BAND_COUNT << " MAX_UP = " << MAX_UP << "MAX_DOWN = " << MAX_DOWN;
barVector.resize( BAND_COUNT, 0 );
roofVector.resize( BAND_COUNT, height() -5 );
roofVelocityVector.resize( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR );
m_roofMem.resize(BAND_COUNT);
m_scope.resize(BAND_COUNT);
//generate a list of values that express amplitudes in range 0-MAX_AMP as ints from 0-height() on log scale
for ( uint x = 0; x < 256; ++x )
{
m_lvlMapper[x] = uint( F * log10( x+1 ) );
}
m_pixBarGradient = QPixmap( height()*COLUMN_WIDTH, height() );
m_pixCompose = QPixmap( size() );
QPainter p( &m_pixBarGradient );
for ( int x=0, r=0x40, g=0x30, b=0xff, r2=255-r;
x < height(); ++x )
{
for ( int y = x; y > 0; --y )
{
const double fraction = (double)y / height();
// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 * fraction) ) );
p.setPen( QColor( r + (int)(r2 * fraction), g, b ) );
p.drawLine( x*COLUMN_WIDTH, height() - y, (x+1)*COLUMN_WIDTH, height() - y );
}
}
setMinimumSize( QSize( BAND_COUNT * COLUMN_WIDTH, 10 ) );
}
void BarAnalyzer::analyze( QPainter& p, const Scope &s )
{
//Analyzer::interpolate( s, m_bands );
Scope &v = m_scope;
Analyzer::interpolate( s, v );
for ( uint i = 0, x = 0, y2; i < v.size(); ++i, x+=COLUMN_WIDTH+1 )
{
//assign pre[log10]'d value
y2 = uint(v[i] * 256); //256 will be optimised to a bitshift //no, it's a float
y2 = m_lvlMapper[ (y2 > 255) ? 255 : y2 ]; //lvlMapper is array of ints with values 0 to height()
int change = y2 - barVector[i];
//using the best of Markey's, piggz and Max's ideas on the way to shift the bars
//we have the following:
// 1. don't adjust shift when doing small up movements
// 2. shift large upwards with a bias towards last value
// 3. fall downwards at a constant pace
/*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter"
//add some dynamics - makes the value slightly closer to what it was last time
y2 = ( barVector[i] + MAX_UP );
//y2 = ( barVector[i] * 2 + y2 ) / 3;
else*/ if ( change < MAX_DOWN )
y2 = barVector[i] + MAX_DOWN;
if ( (int)y2 > roofVector[i] )
{
roofVector[i] = (int)y2;
roofVelocityVector[i] = 1;
}
//remember where we are
barVector[i] = y2;
if ( m_roofMem[i].size() > NUM_ROOFS )
m_roofMem[i].erase( m_roofMem[i].begin() );
//blt last n roofs, a.k.a motion blur
for ( uint c = 0; c < m_roofMem[i].size(); ++c )
//bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ] );
//bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ] );
p.drawPixmap(x, m_roofMem[i][c], m_pixRoof[ NUM_ROOFS - 1 - c ]);
//blt the bar
p.drawPixmap(x, height() - y2,
*gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2);
/*bitBlt( canvas(), x, height() - y2,
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2, Qt::CopyROP );*/
m_roofMem[i].push_back( height() - roofVector[i] - 2 );
//set roof parameters for the NEXT draw
if ( roofVelocityVector[i] != 0 )
{
if ( roofVelocityVector[i] > 32 ) //no reason to do == 32
roofVector[i] -= (roofVelocityVector[i] - 32) / 20; //trivial calculation
if ( roofVector[i] < 0 )
{
roofVector[i] = 0; //not strictly necessary
roofVelocityVector[i] = 0;
}
else ++roofVelocityVector[i];
}
}
}

View File

@ -0,0 +1,57 @@
// Maintainer: Max Howell <max.howell@methylblue.com>
// Authors: Mark Kretschmann & Max Howell (C) 2003-4
// Copyright: See COPYING file that comes with this distribution
//
#ifndef BARANALYZER_H
#define BARANALYZER_H
#include "analyzerbase.h"
typedef std::vector<uint> aroofMemVec;
class BarAnalyzer : public Analyzer::Base
{
public:
BarAnalyzer( QWidget* );
void init();
virtual void analyze( QPainter& p, const Scope& );
//virtual void transform( Scope& );
/**
* Resizes the widget to a new geometry according to @p e
* @param e The resize-event
*/
void resizeEvent( QResizeEvent * e);
uint BAND_COUNT;
int MAX_DOWN;
int MAX_UP;
static const uint ROOF_HOLD_TIME = 48;
static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32;
static const uint NUM_ROOFS = 16;
static const uint COLUMN_WIDTH = 4;
protected:
QPixmap m_pixRoof[NUM_ROOFS];
//vector<uint> m_roofMem[BAND_COUNT];
//Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope every iteration
uint m_lvlMapper[256];
std::vector<aroofMemVec> m_roofMem;
std::vector<uint> barVector; //positions of bars
std::vector<int> roofVector; //positions of roofs
std::vector<uint> roofVelocityVector; //speed that roofs falls
const QPixmap *gradient() const { return &m_pixBarGradient; }
private:
QPixmap m_pixBarGradient;
QPixmap m_pixCompose;
Scope m_scope; //so we don't create a vector every frame
QColor m_bg;
};
#endif

View File

@ -0,0 +1,401 @@
// 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
//
#define DEBUG_PREFIX "BlockAnalyzer"
#include "blockanalyzer.h"
#include <cmath>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QPainter>
#include <cstdlib>
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;
BlockAnalyzer::BlockAnalyzer( QWidget *parent )
: Analyzer::Base( parent, 20, 9 )
, m_columns( 0 ) //uint
, m_rows( 0 ) //uint
, m_y( 0 ) //uint
, m_barPixmap( 1, 1 ) //null qpixmaps cause crashes
, m_topBarPixmap( WIDTH, HEIGHT )
, m_scope( MIN_COLUMNS ) //Scope
, m_store( 1 << 8, 0 ) //vector<uint>
, m_fade_bars( FADE_SIZE ) //vector<QPixmap>
, m_fade_pos( 1 << 8, 50 ) //vector<uint>
, m_fade_intensity( 1 << 8, 32 ) //vector<uint>
{
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 );
// mxcl says null pixmaps cause crashes, so let's play it safe
for ( uint i = 0; i < FADE_SIZE; ++i )
m_fade_bars[i] = QPixmap( 1, 1 );
}
BlockAnalyzer::~BlockAnalyzer()
{
}
void
BlockAnalyzer::resizeEvent( QResizeEvent *e )
{
QWidget::resizeEvent( e );
m_background = QPixmap(size());
const uint oldRows = m_rows;
//all is explained in analyze()..
//+1 to counter -1 in maxSizes, trust me we need this!
m_columns = qMax( uint(double(width()+1) / (WIDTH+1)), MAX_COLUMNS );
m_rows = uint(double(height()+1) / (HEIGHT+1));
//this is the y-offset for drawing from the top of the widget
m_y = (height() - (m_rows * (HEIGHT+1)) + 2) / 2;
m_scope.resize( m_columns );
if( m_rows != oldRows ) {
m_barPixmap = QPixmap( WIDTH, m_rows*(HEIGHT+1) );
for ( uint i = 0; i < FADE_SIZE; ++i )
m_fade_bars[i] = QPixmap( WIDTH, m_rows*(HEIGHT+1) );
m_yscale.resize( m_rows + 1 );
const uint PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat
for( uint z = 0; z < m_rows; ++z )
m_yscale[z] = 1 - (log10( PRE+z ) / log10( PRE+m_rows+PRO ));
m_yscale[m_rows] = 0;
determineStep();
paletteChange( palette() );
}
drawBackground();
}
void
BlockAnalyzer::determineStep()
{
// falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels)
// I calculated the value 30 based on some trial and error
const double fallTime = 30 * m_rows;
m_step = double(m_rows * timeout()) / fallTime;
}
void
BlockAnalyzer::transform( Analyzer::Scope &s ) //pure virtual
{
for( uint x = 0; x < s.size(); ++x )
s[x] *= 2;
float *front = static_cast<float*>( &s.front() );
m_fht->spectrum( front );
m_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 m_scope.size() if large we prevent interpolation of large analyzers, this is good!
s.resize( m_scope.size() <= MAX_COLUMNS/2 ? MAX_COLUMNS/2 : m_scope.size() );
}
void
BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s )
{
// y = 2 3 2 1 0 2
// . . . . # .
// . . . # # .
// # . # # # #
// # # # # # #
//
// visual aid for how this analyzer works.
// y represents the number of blanks
// y starts from the top and increases in units of blocks
// m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
// if it contains 6 elements there are 5 rows in the analyzer
Analyzer::interpolate( s, m_scope );
// Paint the background
p.drawPixmap(0, 0, m_background);
for( uint y, x = 0; x < m_scope.size(); ++x )
{
// determine y
for( y = 0; m_scope[x] < m_yscale[y]; ++y )
;
// this is opposite to what you'd think, higher than y
// means the bar is lower than y (physically)
if( (float)y > m_store[x] )
y = int(m_store[x] += m_step);
else
m_store[x] = y;
// if y is lower than m_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 <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/ ) {
m_fade_pos[x] = y;
m_fade_intensity[x] = FADE_SIZE;
}
if( m_fade_intensity[x] > 0 ) {
const uint offset = --m_fade_intensity[x];
const uint y = m_y + (m_fade_pos[x] * (HEIGHT+1));
p.drawPixmap(x*(WIDTH+1), y, m_fade_bars[offset], 0, 0, WIDTH, height() - y);
}
if( m_fade_intensity[x] == 0 )
m_fade_pos[x] = m_rows;
//REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
p.drawPixmap( x*(WIDTH+1), y*(HEIGHT+1) + m_y, *bar(), 0, y*(HEIGHT+1), bar()->width(), bar()->height() );
}
for( uint x = 0; x < m_store.size(); ++x )
p.drawPixmap(x*(WIDTH+1), int(m_store[x])*(HEIGHT+1) + m_y, m_topBarPixmap );
}
static inline void
adjustToLimits( int &b, int &f, uint &amount )
{
// with a range of 0-255 and maximum adjustment of amount,
// maximise the difference between f and b
if( b < f ) {
if( b > 255 - f ) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
}
else {
if( f > 255 - b ) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
}
}
/**
* Clever contrast function
*
* It will try to adjust the foreground color such that it contrasts well with the background
* 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 )
{
class OutputOnExit {
public:
OutputOnExit( const QColor &color ) : c( color ) {}
~OutputOnExit() { int h,s,v; c.getHsv( &h, &s, &v ); }
private:
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;
int fh, fs, fv;
bg.getHsv( &bh, &bs, &bv );
fg.getHsv( &fh, &fs, &fv );
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;
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;
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
// to allow the colours to be visible and not be shades of grey or black
// 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" );
return fg;
else if( dv > amount / 2 && (bv > 125 && fv > 125) )
// STAMP1( "Sufficient value difference, and hues are compliemtary" );
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;
else
_amount = 0;
}
// test that there is available value to honor our contrast requirement
if( 255 - dv < 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
// see if we need to adjust the hue
if( amount > 0 )
fh += amount; // cycles around;
// STAMP
return QColor::fromHsv(fh, fs, fv);
}
// STAMP
if( fv > bv && bv > amount )
return QColor::fromHsv( fh, fs, bv - amount);
// STAMP
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";
return Qt::blue;
#undef amount
// #undef STAMP
}
void
BlockAnalyzer::paletteChange( const QPalette& ) //virtual
{
const QColor bg = palette().color(QPalette::Background);
const QColor fg = ensureContrast( bg, palette().color(QPalette::Highlight) );
m_topBarPixmap.fill( fg );
const double dr = 15*double(bg.red() - fg.red()) / (m_rows*16);
const double dg = 15*double(bg.green() - fg.green()) / (m_rows*16);
const double db = 15*double(bg.blue() - fg.blue()) / (m_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 < m_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) ) );
{
const QColor bg = palette().color(QPalette::Background).dark( 112 );
//make a complimentary fadebar colour
//TODO dark is not always correct, dumbo!
int h,s,v; palette().color(QPalette::Background).dark( 150 ).getHsv( &h, &s, &v );
const QColor fg( QColor::fromHsv(h + 120, s, v));
const double dr = fg.red() - bg.red();
const double dg = fg.green() - bg.green();
const double db = fg.blue() - bg.blue();
const int r = bg.red(), g = bg.green(), b = bg.blue();
// Precalculate all fade-bar pixmaps
for( uint y = 0; y < FADE_SIZE; ++y ) {
m_fade_bars[y].fill( palette().color(QPalette::Background) );
QPainter f( &m_fade_bars[y] );
for( int z = 0; (uint)z < m_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) ) );
}
}
}
drawBackground();
}
void
BlockAnalyzer::drawBackground()
{
const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark( 112 );
m_background.fill( bg );
QPainter p( &m_background );
for( int x = 0; (uint)x < m_columns; ++x )
for( int y = 0; (uint)y < m_rows; ++y )
p.fillRect( x*(WIDTH+1), y*(HEIGHT+1) + m_y, WIDTH, HEIGHT, bgdark );
}

View File

@ -0,0 +1,62 @@
// Maintainer: Max Howell <mac.howell@methylblue.com>, (C) 2003-5
// Copyright: See COPYING file that comes with this distribution
//
#ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H
#include "analyzerbase.h"
#include <qcolor.h>
class QResizeEvent;
class QMouseEvent;
class QPalette;
/**
* @author Max Howell
*/
class BlockAnalyzer : public Analyzer::Base
{
public:
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;
protected:
virtual void transform( Scope& );
virtual void analyze( QPainter& p, const Scope& );
virtual void resizeEvent( QResizeEvent* );
virtual void paletteChange( const QPalette& );
void drawBackground();
void determineStep();
private:
QPixmap* bar() { return &m_barPixmap; }
uint m_columns, m_rows; //number of rows and columns of blocks
uint m_y; //y-offset from top of widget
QPixmap m_barPixmap;
QPixmap m_topBarPixmap;
QPixmap m_background;
Scope m_scope; //so we don't create a vector every frame
std::vector<float> m_store; //current bar heights
std::vector<float> m_yscale;
//FIXME why can't I namespace these? c++ issue?
std::vector<QPixmap> m_fade_bars;
std::vector<uint> m_fade_pos;
std::vector<int> m_fade_intensity;
float m_step; //rows to fall per frame
};
#endif

View File

@ -0,0 +1,157 @@
// Author: Max Howell <max.howell@methylblue.com>, (C) 2004
// Copyright: See COPYING file that comes with this distribution
#include "amarok.h"
#include "boomanalyzer.h"
#include <cmath>
#include <qlabel.h>
#include <qlayout.h>
#include <qpainter.h>
#include <qslider.h>
#include <qspinbox.h>
BoomAnalyzer::BoomAnalyzer( QWidget *parent )
: Analyzer::Base2D( parent, 10, 9 )
, K_barHeight( 1.271 )//1.471
, F_peakSpeed( 1.103 )//1.122
, F( 1.0 )
, bar_height( BAND_COUNT, 0 )
, peak_height( BAND_COUNT, 0 )
, peak_speed( BAND_COUNT, 0.01 )
, barPixmap( COLUMN_WIDTH, 50 )