Git commit 7c139193a4fcb74a0a3cfdddbe85a3c1168f0de0 by Mark Kretschmann. Committed on 26/05/2013 at 10:49. Pushed by markey into branch 'master'.
Add OpenGL analyzers, and make analyzers configurable (right-click). I've ported two OpenGL analyzers from Amarok 1.x. One of them shows a whirly background, with disco lights in front (named "Disco"). The second one shows something like bouncing balls between two walls (named "Balls"). One of the main advantages of using OpenGL instead of QPainter is the significantly lower CPU load, if you have anything resembling a GPU in your computer. I've also made the height of the widget configurable in 3 different sizes. NOTE: The Balls Analyzer looks somewhat broken. I'll fix that. CCMAIL: amarok-devel@kde.org M +5 -0 images/CMakeLists.txt A +- -- images/ball.png A +- -- images/dot.png A +- -- images/grid.png A +- -- images/wirl1.png A +- -- images/wirl2.png M +133 -9 src/context/applets/analyzer/AnalyzerApplet.cpp M +18 -0 src/context/applets/analyzer/AnalyzerApplet.h M +52 -19 src/context/applets/analyzer/AnalyzerBase.cpp M +45 -15 src/context/applets/analyzer/AnalyzerBase.h A +482 -0 src/context/applets/analyzer/BallsAnalyzer.cpp [License: GPL (v2+)] C +52 -14 src/context/applets/analyzer/BallsAnalyzer.h [from: src/context/applets/analyzer/AnalyzerApplet.h - 051% similarity] M +1 -3 src/context/applets/analyzer/BlockAnalyzer.cpp M +2 -0 src/context/applets/analyzer/CMakeLists.txt A +336 -0 src/context/applets/analyzer/DiscoAnalyzer.cpp [License: GPL (v2+)] C +47 -14 src/context/applets/analyzer/DiscoAnalyzer.h [from: src/context/applets/analyzer/AnalyzerApplet.h - 054% similarity] [License: GPL] http://commits.kde.org/amarok/7c139193a4fcb74a0a3cfdddbe85a3c1168f0de0 diff --git a/images/CMakeLists.txt b/images/CMakeLists.txt index f0db448..c4509d8 100644 --- a/images/CMakeLists.txt +++ b/images/CMakeLists.txt @@ -4,7 +4,9 @@ add_subdirectory( icons ) install(FILES amarok_icon.svg + ball.png default-theme-clean.svg + dot.png emblem-amazon.png emblem-default.png emblem-jamendo.png @@ -19,6 +21,7 @@ install(FILES emblem-ampache-scalable.svgz emblem-scripted.png emblem-scripted-scalable.svgz + grid.png lastfm-default-cover.png echonest.png lastfm.png @@ -38,6 +41,8 @@ install(FILES star.png volume_icon.png volume_muted_icon.png + wirl1.png + wirl2.png service_info_loading1.png service_info_loading2.png service_info_loading3.png diff --git a/images/ball.png b/images/ball.png new file mode 100644 index 0000000..2e406b2 Binary files /dev/null and b/images/ball.png differ diff --git a/images/dot.png b/images/dot.png new file mode 100644 index 0000000..11e3b58 Binary files /dev/null and b/images/dot.png differ diff --git a/images/grid.png b/images/grid.png new file mode 100644 index 0000000..3dbbc25 Binary files /dev/null and b/images/grid.png differ diff --git a/images/wirl1.png b/images/wirl1.png new file mode 100644 index 0000000..e6af564 Binary files /dev/null and b/images/wirl1.png differ diff --git a/images/wirl2.png b/images/wirl2.png new file mode 100644 index 0000000..1307c5e Binary files /dev/null and b/images/wirl2.png differ diff --git a/src/context/applets/analyzer/AnalyzerApplet.cpp b/src/context/applets/analyzer/AnalyzerApplet.cpp index 5c0c189..91b4cb6 100644 --- a/src/context/applets/analyzer/AnalyzerApplet.cpp +++ b/src/context/applets/analyzer/AnalyzerApplet.cpp @@ -18,21 +18,33 @@ #include "AnalyzerApplet.h" +#include "core/support/Amarok.h" +#include "core/support/Debug.h" + +#include "BallsAnalyzer.h" #include "BlockAnalyzer.h" +#include "DiscoAnalyzer.h" -#include <QGraphicsLinearLayout> -#include <QGraphicsProxyWidget> -#include <QGraphicsScene> +#include <QAction> +#include <QGraphicsView> +#include <QMenu> AnalyzerApplet::AnalyzerApplet( QObject* parent, const QVariantList& args ) : Context::Applet( parent, args ) + , m_analyzer( 0 ) { setHasConfigurationInterface( false ); + + connect( this, SIGNAL( geometryChanged() ), this, SLOT( newGeometry() ) ); } AnalyzerApplet::~AnalyzerApplet() -{} +{ + KConfigGroup config = Amarok::config( "Analyzer Applet" ); + config.writeEntry( "Height", (int)m_currentHeight ); + config.writeEntry( "Current Analyzer", m_analyzerName ); +} void AnalyzerApplet::init() @@ -40,14 +52,126 @@ AnalyzerApplet::init() // Call the base implementation. Context::Applet::init(); - BlockAnalyzer *analyzer = new BlockAnalyzer( 0 ); + m_analyzerNames["Balls"] = i18n( "Balls (OpenGL)" ); + m_analyzerNames["Blocky"] = i18n( "Blocky" ); + m_analyzerNames["Disco"] = i18n( "Disco (OpenGL)" ); + + KConfigGroup config = Amarok::config( "Analyzer Applet" ); + + if( config.readEntry( "Height", (int)Small ) == Small ) + setHeightSmall(); + if( config.readEntry( "Height", (int)Small ) == Medium ) + setHeightMedium(); + if( config.readEntry( "Height", (int)Small ) == Tall ) + setHeightTall(); + + setCurrentAnalyzer( config.readEntry( "Current Analyzer", "Blocky" ) ); +} + +void AnalyzerApplet::newGeometry() +{ + m_analyzer->setGeometry( geometry().toRect() ); +} + +QList<QAction *> +AnalyzerApplet::contextualActions () +{ + QList<QAction*> actions; + QAction *action; + + QMenu *heightMenu = new QMenu( i18n( "Height" ), view() ); + actions << heightMenu->menuAction(); + + QActionGroup *heightActions = new QActionGroup( this ); + + action = heightMenu->addAction( i18n( "Small" ) ); + action->setCheckable( true ); + action->setChecked( m_currentHeight == Small ); + action->setActionGroup( heightActions ); + connect( action, SIGNAL( triggered() ), this, SLOT( setHeightSmall() ) ); + action = heightMenu->addAction( i18n( "Medium" ) ); + action->setCheckable( true ); + action->setChecked( m_currentHeight == Medium ); + action->setActionGroup( heightActions ); + connect( action, SIGNAL( triggered() ), this, SLOT( setHeightMedium() ) ); + action = heightMenu->addAction( i18n( "Tall" ) ); + action->setCheckable( true ); + action->setChecked( m_currentHeight == Tall ); + action->setActionGroup( heightActions ); + connect( action, SIGNAL( triggered() ), this, SLOT( setHeightTall() ) ); + + action = new QAction( this ); + action->setSeparator( true ); + actions << action; + + QActionGroup *analyzerActions = new QActionGroup( this ); + connect( analyzerActions, SIGNAL( triggered( QAction* ) ), this, SLOT( analyzerAction( QAction* ) ) ); + + QMap<QString, QString>::const_iterator i = m_analyzerNames.constBegin(); + while ( i != m_analyzerNames.constEnd() ) { + action = new QAction( i.value(), this ); + action->setData( i.key() ); + action->setCheckable( true ); + action->setChecked( m_analyzerName == i.key() ); + action->setActionGroup( analyzerActions ); + actions << action; + i++; + } + + return actions; +} + +void +AnalyzerApplet::setHeightSmall() +{ + setMinimumHeight( 100 ); + m_currentHeight = Small; +} + +void +AnalyzerApplet::setHeightMedium() +{ + setMinimumHeight( 150 ); + m_currentHeight = Medium; +} + +void +AnalyzerApplet::setHeightTall() +{ + setMinimumHeight( 200 ); + m_currentHeight = Tall; +} + +void +AnalyzerApplet::analyzerAction( QAction *action ) +{ + DEBUG_BLOCK + + setCurrentAnalyzer( action->data().toString() ); +} + +void +AnalyzerApplet::setCurrentAnalyzer( const QString &name ) +{ + DEBUG_BLOCK + + debug() << "name: " << name; + + if( m_analyzerName == name ) + return; - QGraphicsProxyWidget *proxy = scene()->addWidget( analyzer ); + delete m_analyzer; + m_analyzerName = name; - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical, this ); - layout->addItem( proxy ); + if( name == "Balls" ) + m_analyzer = new BallsAnalyzer( view()->viewport() ); + else if( name == "Blocky" ) + m_analyzer = new BlockAnalyzer( view()->viewport() ); + else if( name == "Disco" ) + m_analyzer = new DiscoAnalyzer( view()->viewport() ); - updateConstraints(); + newGeometry(); + m_analyzer->show(); } #include "AnalyzerApplet.moc" diff --git a/src/context/applets/analyzer/AnalyzerApplet.h b/src/context/applets/analyzer/AnalyzerApplet.h index b47246f..45912f9 100644 --- a/src/context/applets/analyzer/AnalyzerApplet.h +++ b/src/context/applets/analyzer/AnalyzerApplet.h @@ -26,11 +26,29 @@ class AnalyzerApplet : public Context::Applet Q_OBJECT public: + enum WidgetHeight { Small = 0, Medium = 1, Tall = 2 }; + AnalyzerApplet( QObject* parent, const QVariantList& args ); virtual ~AnalyzerApplet(); public slots: virtual void init(); + +private slots: + void newGeometry(); + void setHeightSmall(); + void setHeightMedium(); + void setHeightTall(); + void analyzerAction( QAction* ); + +private: + void setCurrentAnalyzer( const QString &name); + QList<QAction *> contextualActions(); + + QWidget *m_analyzer; + QString m_analyzerName; + QMap<QString, QString> m_analyzerNames; + WidgetHeight m_currentHeight; }; AMAROK_EXPORT_APPLET( analyzer, AnalyzerApplet ) diff --git a/src/context/applets/analyzer/AnalyzerBase.cpp b/src/context/applets/analyzer/AnalyzerBase.cpp index 420623b..7ee4992 100644 --- a/src/context/applets/analyzer/AnalyzerBase.cpp +++ b/src/context/applets/analyzer/AnalyzerBase.cpp @@ -23,7 +23,6 @@ #include <QEvent> // event() #include <QPainter> -#include <Phonon/AudioOutput> // INSTRUCTIONS Base2D @@ -36,16 +35,15 @@ //can't mod scope in analyze you have to use transform -Analyzer::Base::Base( QWidget *parent, uint scopeSize ) - : QWidget( parent ) +template<class W> +Analyzer::Base<W>::Base( QWidget *parent, uint scopeSize ) + : W( parent ) , m_fht( new FHT( scopeSize ) ) { - connect( EngineController::instance(), SIGNAL( audioDataReady( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ), - this, SLOT( drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ) ); } -void -Analyzer::Base::transform( QVector<float> &scope ) //virtual +template<class W> void +Analyzer::Base<W>::transform( QVector<float> &scope ) //virtual { //this is a standard transformation that should give //an FFT scope that has bands for pretty analyzers @@ -64,8 +62,8 @@ Analyzer::Base::transform( QVector<float> &scope ) //virtual delete [] f; } -void -Analyzer::Base::drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ) +template<class W> void +Analyzer::Base<W>::drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ) { if( thescope.isEmpty() ) return; @@ -94,8 +92,8 @@ Analyzer::Base::drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector< scope.resize( m_fht->size() ); } -int -Analyzer::Base::resizeExponent( int exp ) +template<class W> int +Analyzer::Base<W>::resizeExponent( int exp ) { if( exp < 3 ) exp = 3; @@ -110,8 +108,8 @@ Analyzer::Base::resizeExponent( int exp ) return exp; } -int -Analyzer::Base::resizeForBands( int bands ) +template<class W> int +Analyzer::Base<W>::resizeForBands( int bands ) { int exp; if( bands <= 8 ) @@ -131,12 +129,12 @@ Analyzer::Base::resizeForBands( int bands ) return m_fht->size() / 2; } -void -Analyzer::Base::paused() //virtual +template<class W> void +Analyzer::Base<W>::paused() //virtual {} -void -Analyzer::Base::demo() //virtual +template<class W> void +Analyzer::Base<W>::demo() //virtual { static int t = 201; //FIXME make static to namespace perhaps // qDebug() << Q_FUNC_INFO << t; @@ -160,15 +158,22 @@ Analyzer::Base::demo() //virtual Analyzer::Base2D::Base2D( QWidget *parent, uint scopeSize ) - : Base( parent, scopeSize ) + : Base<QWidget>( parent, scopeSize ) { connect( EngineController::instance(), SIGNAL( playbackStateChanged() ), this, SLOT( playbackStateChanged() ) ); + connect( EngineController::instance(), SIGNAL( audioDataReady( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ), + this, SLOT( draw( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ) ); + m_timer.setInterval( 34 ); connect( &m_timer, SIGNAL( timeout() ), this, SLOT( demo() ) ); enableDemo( !EngineController::instance()->isPlaying() ); - QTimer::singleShot( 0, this, SLOT( init() ) ); // needs to know the size + + QTimer *timer = new QTimer( this ); + timer->setInterval( 20 ); + connect( timer, SIGNAL( timeout() ), this, SLOT( update() ) ); + timer->start(); } void Analyzer::Base2D::resizeEvent( QResizeEvent *e ) @@ -193,6 +198,34 @@ void Analyzer::Base2D::playbackStateChanged() enableDemo( !EngineController::instance()->isPlaying() ); } + + +Analyzer::Base3D::Base3D( QWidget *parent, uint scopeSize ) + : Base<QGLWidget>( parent, scopeSize ) +{ + connect( EngineController::instance(), SIGNAL( playbackStateChanged() ), this, SLOT( playbackStateChanged() ) ); + + connect( EngineController::instance(), SIGNAL( audioDataReady( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ), + this, SLOT( draw( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > & ) ) ); + + m_timer.setInterval( 34 ); + connect( &m_timer, SIGNAL( timeout() ), this, SLOT( demo() ) ); + + enableDemo( !EngineController::instance()->isPlaying() ); + + QTimer *timer = new QTimer( this ); + timer->setInterval( 20 ); + connect( timer, SIGNAL( timeout() ), this, SLOT( updateGL() ) ); + timer->start(); +} + +void Analyzer::Base3D::playbackStateChanged() +{ + enableDemo( !EngineController::instance()->isPlaying() ); +} + + + void Analyzer::interpolate( const QVector<float> &inVec, QVector<float> &outVec ) //static { diff --git a/src/context/applets/analyzer/AnalyzerBase.h b/src/context/applets/analyzer/AnalyzerBase.h index 75dc422..568cd2f 100644 --- a/src/context/applets/analyzer/AnalyzerBase.h +++ b/src/context/applets/analyzer/AnalyzerBase.h @@ -23,13 +23,17 @@ #endif #include "fht.h" //stack allocated and convenience -#include <qpixmap.h> //stack allocated and convenience -#include <qtimer.h> //stack allocated -#include <qwidget.h> //baseclass + +#include <QGLWidget> +#include <QPixmap> //stack allocated and convenience +#include <QTimer> //stack allocated +#include <QWidget> //baseclass + #include <vector> //included for convenience #include <phonon/audiodataoutput.h> + class QEvent; class QPaintEvent; class QResizeEvent; @@ -40,12 +44,10 @@ namespace Analyzer typedef std::vector<float> Scope; -class Base : public QWidget +template<class W> class Base : public W { - Q_OBJECT - -public slots: - void drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ); +public: + virtual void drawFrame( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ); protected: Base( QWidget*, uint = 7 ); @@ -54,29 +56,30 @@ protected: delete m_fht; } + void demo(); + int resizeExponent( int ); int resizeForBands( int ); virtual void transform( QVector<float>& ); virtual void analyze( const QVector<float>& ) = 0; virtual void paused(); -public slots: - void demo(); - protected: FHT *m_fht; }; -class Base2D : public Base +class Base2D : public Base<QWidget> { Q_OBJECT + public: const QPixmap *canvas() const { return &m_canvas; } + private slots: void enableDemo( bool enable ) { @@ -84,6 +87,11 @@ private slots: } void playbackStateChanged(); + void draw( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ) + { + drawFrame( thescope ); + } + protected: Base2D( QWidget*, uint scopeSize = 7 ); @@ -95,14 +103,36 @@ protected: void paintEvent( QPaintEvent* ); void resizeEvent( QResizeEvent* ); -protected slots: - virtual void init() {} - private: QPixmap m_canvas; QTimer m_timer; }; + +class Base3D : public Base<QGLWidget> +{ + Q_OBJECT + +private slots: + void enableDemo( bool enable ) + { + enable ? m_timer.start() : m_timer.stop(); + } + void playbackStateChanged(); + + void draw( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &thescope ) + { + drawFrame( thescope ); + } + +protected: + Base3D( QWidget*, uint scopeSize = 7 ); + +private: + QTimer m_timer; +}; + + class Factory { //Currently this is a rather small class, its only purpose diff --git a/src/context/applets/analyzer/BallsAnalyzer.cpp b/src/context/applets/analyzer/BallsAnalyzer.cpp new file mode 100644 index 0000000..0896970 --- /dev/null +++ b/src/context/applets/analyzer/BallsAnalyzer.cpp @@ -0,0 +1,482 @@ +/**************************************************************************************** + * Copyright (c) 2004 Enrico Ros <eros....@email.it> * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +#include "BallsAnalyzer.h" + +#include <KStandardDirs> + +#include <QImage> + +#include <cmath> +#include <cstdlib> +#include <sys/time.h> + + +#ifndef HAVE_FABSF +inline float fabsf( float f ) +{ + return f < 0.f ? -f : f; +} +#endif + + +class Ball +{ +public: + Ball() : x( drand48() - drand48() ), y( 1 - 2.0 * drand48() ), + z( drand48() ), vx( 0.0 ), vy( 0.0 ), vz( 0.0 ), + mass( 0.01 + drand48() / 10.0 ) + //,color( (float[3]) { 0.0, drand48()*0.5, 0.7 + drand48() * 0.3 } ) + { + //this is because GCC < 3.3 can't compile the above line, we aren't sure why though + color[0] = 0.0; color[1] = drand48() * 0.5; color[2] = 0.7 + drand48() * 0.3; + }; + + float x, y, z, vx, vy, vz, mass; + float color[3]; + + void updatePhysics( float dT ) + { + x += vx * dT; // position + y += vy * dT; // position + z += vz * dT; // position + if( y < -0.8 ) vy = fabsf( vy ); + if( y > 0.8 ) vy = -fabsf( vy ); + if( z < 0.1 ) vz = fabsf( vz ); + if( z > 0.9 ) vz = -fabsf( vz ); + vx += ( ( x > 0 ) ? 4.94 : -4.94 ) * dT; // G-force + vx *= ( 1 - 2.9 * dT ); // air friction + vy *= ( 1 - 2.9 * dT ); // air friction + vz *= ( 1 - 2.9 * dT ); // air friction + } +}; + +class Paddle +{ +public: + Paddle( float xPos ) : onLeft( xPos < 0 ), mass( 1.0 ), + X( xPos ), x( xPos ), vx( 0.0 ) {}; + + void updatePhysics( float dT ) + { + x += vx * dT; // posision + vx += ( 1300 * ( X - x ) / mass ) * dT; // elasticity + vx *= ( 1 - 4.0 * dT ); // air friction + } + + void renderGL() + { + glBegin( GL_TRIANGLE_STRIP ); + glColor3f( 0.0f, 0.1f, 0.3f ); + glVertex3f( x, -1.0f, 0.0 ); + glVertex3f( x, 1.0f, 0.0 ); + glColor3f( 0.1f, 0.2f, 0.6f ); + glVertex3f( x, -1.0f, 1.0 ); + glVertex3f( x, 1.0f, 1.0 ); + glEnd(); + } + + void bounce( Ball * ball ) + { + if( onLeft && ball->x < x ) + { + ball->vx = vx * mass / ( mass + ball->mass ) + fabsf( ball->vx ); + ball->vy = ( drand48() - drand48() ) * 1.8; + ball->vz = ( drand48() - drand48() ) * 0.9; + ball->x = x; + } + else if( !onLeft && ball->x > x ) + { + ball->vx = vx * mass / ( mass + ball->mass ) - fabsf( ball->vx ); + ball->vy = ( drand48() - drand48() ) * 1.8; + ball->vz = ( drand48() - drand48() ) * 0.9; + ball->x = x; + } + } + + void impulse( float strength ) + { + if( ( onLeft && strength > vx ) || ( !onLeft && strength < vx ) ) + vx += strength; + } + +private: + bool onLeft; + float mass, X, x, vx; +}; + + +BallsAnalyzer::BallsAnalyzer( QWidget *parent ): + Analyzer::Base3D( parent ) +{ + setObjectName( "Balls" ); + + //initialize openGL context before managing GL calls + makeCurrent(); + loadTexture( KStandardDirs::locate( "data", "amarok/images/ball.png" ), ballTexture ); + loadTexture( KStandardDirs::locate( "data", "amarok/images/grid.png" ), gridTexture ); + + leftPaddle = new Paddle( -1.0 ); + rightPaddle = new Paddle( 1.0 ); + for( int i = 0; i < NUMBER_OF_BALLS; i++ ) + balls.append( new Ball() ); + + show.colorK = 0.0; + show.gridScrollK = 0.0; + show.gridEnergyK = 0.0; + show.camRot = 0.0; + show.camRoll = 0.0; + show.peakEnergy = 1.0; + frame.silence = true; + frame.energy = 0.0; + frame.dEnergy = 0.0; +} + +BallsAnalyzer::~BallsAnalyzer() +{ + freeTexture( ballTexture ); + freeTexture( gridTexture ); + delete leftPaddle; + delete rightPaddle; + + foreach( Ball * ball, balls ) + delete ball; +} + +void BallsAnalyzer::initializeGL() +{ + // Set a smooth shade model + glShadeModel( GL_SMOOTH ); + + // Disable depth test (all is drawn 'z-sorted') + glDisable( GL_DEPTH_TEST ); + + // Set blending function (Alpha addition) + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + + // Clear frame with a black background + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); +} + +void BallsAnalyzer::resizeGL( int w, int h ) +{ + // Setup screen. We're going to manually do the perspective projection + glViewport( 0, 0, ( GLint )w, ( GLint )h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 4.5f ); + + // Get the aspect ratio of the screen to draw 'circular' particles + float ratio = ( float )w / ( float )h; + if( ratio >= 1.0 ) + { + unitX = 0.34 / ratio; + unitY = 0.34; + } + else + { + unitX = 0.34; + unitY = 0.34 * ratio; + } + + // Get current timestamp. + timeval tv; + gettimeofday( &tv, NULL ); + show.timeStamp = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; +} + +void BallsAnalyzer::paused() +{ + //analyze( Scope() ); +} + +void BallsAnalyzer::analyze( const QVector<float> &s ) +{ + // compute the dTime since the last call + timeval tv; + gettimeofday( &tv, NULL ); + double currentTime = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; + show.dT = currentTime - show.timeStamp; + show.timeStamp = currentTime; + + // compute energy integrating frame's spectrum + if( !s.empty() ) + { + int bands = s.size(); + float currentEnergy = 0, + maxValue = 0; + // integrate spectrum -> energy + for( int i = 0; i < bands; i++ ) + { + float value = s[i]; + currentEnergy += value; + if( value > maxValue ) + maxValue = value; + } + currentEnergy *= 100.0 / ( float )bands; + // emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds) + show.peakEnergy = 1.0 + ( show.peakEnergy - 1.0 ) * exp( - show.dT / 10.0 ); + if( currentEnergy > show.peakEnergy ) + show.peakEnergy = currentEnergy; + // check for silence + frame.silence = currentEnergy < 0.001; + // normalize frame energy against peak energy and compute frame stats + currentEnergy /= show.peakEnergy; + frame.dEnergy = currentEnergy - frame.energy; + frame.energy = currentEnergy; + } + else + frame.silence = true; +} + +void BallsAnalyzer::paintGL() +{ + // limit max dT to 0.05 and update color and scroll constants + if( show.dT > 0.05 ) + show.dT = 0.05; + show.colorK += show.dT * 0.4; + if( show.colorK > 3.0 ) + show.colorK -= 3.0; + show.gridScrollK += 0.2 * show.peakEnergy * show.dT; + + // Switch to MODEL matrix and clear screen + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glClear( GL_COLOR_BUFFER_BIT ); + + // Draw scrolling grid + if( ( show.gridEnergyK > 0.05 ) || ( !frame.silence && frame.dEnergy < -0.3 ) ) + { + show.gridEnergyK *= exp( -show.dT / 0.1 ); + if( -frame.dEnergy > show.gridEnergyK ) + show.gridEnergyK = -frame.dEnergy * 2.0; + float gridColor[4] = { 0.0, 1.0, 0.6, show.gridEnergyK }; + drawScrollGrid( show.gridScrollK, gridColor ); + } + + // Roll camera up/down handling the beat + show.camRot += show.camRoll * show.dT; // posision + show.camRoll -= 400 * show.camRot * show.dT; // elasticity + show.camRoll *= ( 1 - 2.0 * show.dT ); // friction + if( !frame.silence && frame.dEnergy > 0.4 ) + show.camRoll += show.peakEnergy * 2.0; + glRotatef( show.camRoll / 2.0, 1, 0, 0 ); + + // Translate the drawing plane + glTranslatef( 0.0f, 0.0f, -1.8f ); + + // Draw upper/lower planes and paddles + drawHFace( -1.0 ); + drawHFace( 1.0 ); + leftPaddle->renderGL(); + rightPaddle->renderGL(); + + // Draw Balls + if( ballTexture ) + { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, ballTexture ); + } + else + glDisable( GL_TEXTURE_2D ); + glEnable( GL_BLEND ); + foreach( Ball * ball, balls ) + { + float color[3], + angle = show.colorK; + // Rotate the color based on 'angle' value [0,3) + if( angle < 1.0 ) + { + color[ 0 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; + color[ 1 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; + color[ 2 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; + } + else if( angle < 2.0 ) + { + angle -= 1.0; + color[ 0 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; + color[ 1 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; + color[ 2 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; + } + else + { + angle -= 2.0; + color[ 0 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; + color[ 1 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; + color[ 2 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; + } + // Draw the dot and update its physics also checking at bounces + glColor3fv( color ); + drawDot3s( ball->x, ball->y, ball->z, 1.0 ); + ball->updatePhysics( show.dT ); + if( ball->x < 0 ) + leftPaddle->bounce( ball ); + else + rightPaddle->bounce( ball ); + } + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + + // Update physics of paddles + leftPaddle->updatePhysics( show.dT ); + rightPaddle->updatePhysics( show.dT ); + if( !frame.silence ) + { + leftPaddle->impulse( frame.energy * 3.0 + frame.dEnergy * 6.0 ); + rightPaddle->impulse( -frame.energy * 3.0 - frame.dEnergy * 6.0 ); + } +} + +void BallsAnalyzer::drawDot3s( float x, float y, float z, float size ) +{ + // Circular XY dot drawing functions + float sizeX = size * unitX, + sizeY = size * unitY, + pXm = x - sizeX, + pXM = x + sizeX, + pYm = y - sizeY, + pYM = y + sizeY; + // Draw the Dot + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, pYm, z ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, pYM, z ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, pYM, z ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, pYm, z ); + glEnd(); + + // Shadow XZ drawing functions + float sizeZ = size / 10.0, + pZm = z - sizeZ, + pZM = z + sizeZ, + currentColor[4]; + glGetFloatv( GL_CURRENT_COLOR, currentColor ); + float alpha = currentColor[3], + topSide = ( y + 1 ) / 4, + bottomSide = ( 1 - y ) / 4; + // Draw the top shadow + currentColor[3] = topSide * topSide * alpha; + glColor4fv( currentColor ); + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, 1, pZm ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, 1, pZM ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, 1, pZM ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, 1, pZm ); + glEnd(); + // Draw the bottom shadow + currentColor[3] = bottomSide * bottomSide * alpha; + glColor4fv( currentColor ); + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, -1, pZm ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, -1, pZM ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, -1, pZM ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, -1, pZm ); + glEnd(); +} + +void BallsAnalyzer::drawHFace( float y ) +{ + glBegin( GL_TRIANGLE_STRIP ); + glColor3f( 0.0f, 0.1f, 0.2f ); + glVertex3f( -1.0f, y, 0.0 ); + glVertex3f( 1.0f, y, 0.0 ); + glColor3f( 0.1f, 0.6f, 0.5f ); + glVertex3f( -1.0f, y, 2.0 ); + glVertex3f( 1.0f, y, 2.0 ); + glEnd(); +} + +void BallsAnalyzer::drawScrollGrid( float scroll, float color[4] ) +{ + if( !gridTexture ) + return; + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glTranslatef( 0.0, -scroll, 0.0 ); + glMatrixMode( GL_MODELVIEW ); + float backColor[4] = { 1.0, 1.0, 1.0, 0.0 }; + for( int i = 0; i < 3; i++ ) + backColor[ i ] = color[ i ]; + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, gridTexture ); + glEnable( GL_BLEND ); + glBegin( GL_TRIANGLE_STRIP ); + glColor4fv( color ); // top face + glTexCoord2f( 0.0f, 1.0f ); + glVertex3f( -1.0f, 1.0f, -1.0f ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex3f( 1.0f, 1.0f, -1.0f ); + glColor4fv( backColor ); // central points + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( -1.0f, 0.0f, -3.0f ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex3f( 1.0f, 0.0f, -3.0f ); + glColor4fv( color ); // bottom face + glTexCoord2f( 0.0f, 1.0f ); + glVertex3f( -1.0f, -1.0f, -1.0f ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex3f( 1.0f, -1.0f, -1.0f ); + glEnd(); + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); +} + +bool BallsAnalyzer::loadTexture( QString fileName, GLuint& textureID ) +{ + //reset texture ID to the default EMPTY value + textureID = 0; + + //load image + QImage tmp; + if( !tmp.load( fileName ) ) + return false; + + //convert it to suitable format (flipped RGBA) + QImage texture = QGLWidget::convertToGLFormat( tmp ); + if( texture.isNull() ) + return false; + + //get texture number and bind loaded image to that texture + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() ); + return true; +} + +void BallsAnalyzer::freeTexture( GLuint& textureID ) +{ + if( textureID > 0 ) + glDeleteTextures( 1, &textureID ); + textureID = 0; +} + diff --git a/src/context/applets/analyzer/AnalyzerApplet.h b/src/context/applets/analyzer/BallsAnalyzer.h similarity index 51% copy from src/context/applets/analyzer/AnalyzerApplet.h copy to src/context/applets/analyzer/BallsAnalyzer.h index b47246f..0bff339 100644 --- a/src/context/applets/analyzer/AnalyzerApplet.h +++ b/src/context/applets/analyzer/BallsAnalyzer.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * Copyright (c) 2013 Mark Kretschmann <kretschm...@kde.org> * + * Copyright (c) 2004 Enrico Ros <eros....@email.it> * * * * 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 * @@ -14,25 +14,63 @@ * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ -#ifndef ANALYZER_APPLET_H -#define ANALYZER_APPLET_H +#ifndef BALLS_ANALYZER_H +#define BALLS_ANALYZER_H -#include "context/Applet.h" -#include "context/DataEngine.h" +#include "AnalyzerBase.h" -class AnalyzerApplet : public Context::Applet -{ - Q_OBJECT +class QWidget; +class Ball; +class Paddle; +class BallsAnalyzer : public Analyzer::Base3D +{ public: - AnalyzerApplet( QObject* parent, const QVariantList& args ); - virtual ~AnalyzerApplet(); + BallsAnalyzer( QWidget * ); + ~BallsAnalyzer(); + void analyze( const QVector<float> & ); + void paused(); -public slots: - virtual void init(); -}; +protected: + void initializeGL(); + void resizeGL( int w, int h ); + void paintGL(); + +private: + struct ShowProperties + { + double timeStamp; + double dT; + float colorK; + float gridScrollK; + float gridEnergyK; + float camRot; + float camRoll; + float peakEnergy; + } show; + + struct FrameProperties + { + bool silence; + float energy; + float dEnergy; + } frame; -AMAROK_EXPORT_APPLET( analyzer, AnalyzerApplet ) + static const int NUMBER_OF_BALLS = 16; + + QList<Ball*> balls; + Paddle * leftPaddle, * rightPaddle; + float unitX, unitY; + GLuint ballTexture; + GLuint gridTexture; + + void drawDot3s( float x, float y, float z, float size ); + void drawHFace( float y ); + void drawScrollGrid( float scroll, float color[4] ); + + bool loadTexture( QString file, GLuint& textureID ); + void freeTexture( GLuint& textureID ); +}; #endif diff --git a/src/context/applets/analyzer/BlockAnalyzer.cpp b/src/context/applets/analyzer/BlockAnalyzer.cpp index d171437..3316c28 100644 --- a/src/context/applets/analyzer/BlockAnalyzer.cpp +++ b/src/context/applets/analyzer/BlockAnalyzer.cpp @@ -43,9 +43,8 @@ BlockAnalyzer::BlockAnalyzer( QWidget *parent ) , 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 + setObjectName( "Blocky" ); setMaximumWidth( MAX_COLUMNS * ( WIDTH + 1 ) - 1 ); - setMaximumHeight( MIN_ROWS * ( HEIGHT + 1 ) - 1 ); } BlockAnalyzer::~BlockAnalyzer() @@ -124,7 +123,6 @@ void BlockAnalyzer::analyze( const QVector<float> &s ) { Analyzer::interpolate( s, m_scope ); - update(); } void diff --git a/src/context/applets/analyzer/CMakeLists.txt b/src/context/applets/analyzer/CMakeLists.txt index 0fd9d5e..e793742 100644 --- a/src/context/applets/analyzer/CMakeLists.txt +++ b/src/context/applets/analyzer/CMakeLists.txt @@ -7,7 +7,9 @@ include_directories( set(analyzer_SRCS AnalyzerApplet.cpp AnalyzerBase.cpp + BallsAnalyzer.cpp BlockAnalyzer.cpp + DiscoAnalyzer.cpp fht.cpp ) diff --git a/src/context/applets/analyzer/DiscoAnalyzer.cpp b/src/context/applets/analyzer/DiscoAnalyzer.cpp new file mode 100644 index 0000000..6fc1c04 --- /dev/null +++ b/src/context/applets/analyzer/DiscoAnalyzer.cpp @@ -0,0 +1,336 @@ +/**************************************************************************************** + * Copyright (c) 2004 Enrico Ros <eros....@email.it> * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +#include "DiscoAnalyzer.h" + +#include "core/support/Debug.h" + +#include <QImage> +#include <KStandardDirs> + +#include <cmath> +#include <cstdlib> +#include <sys/time.h> + + +DiscoAnalyzer::DiscoAnalyzer( QWidget *parent ): + Analyzer::Base3D( parent ) +{ + setObjectName( "Disco" ); + + //initialize openGL context before managing GL calls + makeCurrent(); + loadTexture( KStandardDirs::locate( "data", "amarok/images/dot.png" ), dotTexture ); + loadTexture( KStandardDirs::locate( "data", "amarok/images/wirl1.png" ), w1Texture ); + loadTexture( KStandardDirs::locate( "data", "amarok/images/wirl2.png" ), w2Texture ); + + showStruct.paused = true; + showStruct.pauseTimer = 0.0; + showStruct.rotDegrees = 0.0; + frame.rotDegrees = 0.0; +} + +DiscoAnalyzer::~DiscoAnalyzer() +{ + freeTexture( dotTexture ); + freeTexture( w1Texture ); + freeTexture( w2Texture ); +} + +void DiscoAnalyzer::initializeGL() +{ + // Set a smooth shade model + glShadeModel( GL_SMOOTH ); + + // Disable depth test (all is drawn on a 2d plane) + glDisable( GL_DEPTH_TEST ); + + // Set blend parameters for 'composting alpha' + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); //superpose + //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); //fade + glEnable( GL_BLEND ); + + // Clear frame with a black background + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT ); +} + +void DiscoAnalyzer::resizeGL( int w, int h ) +{ + // Setup screen. We're going to manually do the perspective projection + glViewport( 0, 0, ( GLint )w, ( GLint )h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( -10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f ); + + // Get the aspect ratio of the screen to draw 'cicular' particles + float ratio = ( float )w / ( float )h, + eqPixH = 60, + eqPixW = 80; + if( ratio >= ( 4.0 / 3.0 ) ) + { + unitX = 10.0 / ( eqPixH * ratio ); + unitY = 10.0 / eqPixH; + } + else + { + unitX = 10.0 / eqPixW; + unitY = 10.0 / ( eqPixW / ratio ); + } + + // Get current timestamp. + timeval tv; + gettimeofday( &tv, NULL ); + showStruct.timeStamp = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; +} + +void DiscoAnalyzer::paused() +{ + // FIXME +// analyze( Scope() ); +} + +void DiscoAnalyzer::analyze( const QVector<float> &s ) +{ + bool haveNoData = s.empty(); + + // if we're going into pause mode, clear timers. + if( !showStruct.paused && haveNoData ) + showStruct.pauseTimer = 0.0; + + // if we have got data, interpolate it (asking myself why I'm doing it here..) + if( !( showStruct.paused = haveNoData ) ) + { + int bands = s.size(), + lowbands = bands / 4, + hibands = bands / 3, + midbands = bands - lowbands - hibands; Q_UNUSED( midbands ); + float currentEnergy = 0, + currentMeanBand = 0, + maxValue = 0; + for( int i = 0; i < bands; i++ ) + { + float value = s[i]; + currentEnergy += value; + currentMeanBand += ( float )i * value; + if( value > maxValue ) + maxValue = value; + } + frame.silence = currentEnergy < 0.001; + if( !frame.silence ) + { + frame.meanBand = 100.0 * currentMeanBand / ( currentEnergy * bands ); + currentEnergy = 100.0 * currentEnergy / ( float )bands; + frame.dEnergy = currentEnergy - frame.energy; + frame.energy = currentEnergy; +// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy, frame.meanBand, maxValue ); + } + else + frame.energy = 0.0; + } +} + +void DiscoAnalyzer::paintGL() +{ + // Compute the dT since the last call to paintGL and update timings + timeval tv; + gettimeofday( &tv, NULL ); + double currentTime = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; + showStruct.dT = currentTime - showStruct.timeStamp; + showStruct.timeStamp = currentTime; + + // Clear frame + glClear( GL_COLOR_BUFFER_BIT ); + + // Shitch to MODEL matrix and reset it to default + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Fade the previous drawings. + /* glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glBegin( GL_TRIANGLE_STRIP ); + glColor4f( 0.0f, 0.0f, 0.0f, 0.2f ); + glVertex2f( 10.0f, 10.0f ); + glVertex2f( -10.0f, 10.0f ); + glVertex2f( 10.0f, -10.0f ); + glVertex2f( -10.0f, -10.0f ); + glEnd();*/ + + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_TEXTURE_2D ); + float alphaN = showStruct.paused ? 0.2 : ( frame.energy / 10.0 ), + alphaP = showStruct.paused ? 1.0 : ( 1 - frame.energy / 20.0 ); + if( alphaN > 1.0 ) + alphaN = 1.0; + if( alphaP < 0.1 ) + alphaP = 0.1; + glBindTexture( GL_TEXTURE_2D, w2Texture ); + setTextureMatrix( showStruct.rotDegrees, 0.707 * alphaP ); + glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + glBindTexture( GL_TEXTURE_2D, w1Texture ); + setTextureMatrix( -showStruct.rotDegrees * 2, 0.707 ); + glColor4f( 1.0f, 1.0f, 1.0f, alphaN ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + setTextureMatrix( 0.0, 0.0 ); + glDisable( GL_TEXTURE_2D ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + + // Here begins the real draw loop + // some updates to the showStruct + showStruct.rotDegrees += 40.0 * showStruct.dT; + frame.rotDegrees += 80.0 * showStruct.dT; + + // handle the 'pause' status + if( showStruct.paused ) + { + if( showStruct.pauseTimer > 0.5 ) + { + if( showStruct.pauseTimer > 0.6 ) + showStruct.pauseTimer -= 0.6; + drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); + drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); + } + showStruct.pauseTimer += showStruct.dT; + return; + } + + if( dotTexture ) + { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, dotTexture ); + } + else + glDisable( GL_TEXTURE_2D ); + + glLoadIdentity(); +// glRotatef( -frame.rotDegrees, 0,0,1 ); + glBegin( GL_QUADS ); +// Particle * particle = particleList.first(); +// for (; particle; particle = particleList.next()) + { + glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); + drawDot( 0, 0, qMax( 10.0, ( 10.0 * frame.energy ) ) ); + glColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); + drawDot( 6, 0, qMax( 10.0, ( 5.0 * frame.energy ) ) ); + glColor4f( 0.0f, 0.4f, 1.0f, 1.0f ); + drawDot( -6, 0, qMax( 10.0, ( 5.0 * frame.energy ) ) ); + } + glEnd(); +} + +void DiscoAnalyzer::drawDot( float x, float y, float size ) +{ + float sizeX = size * unitX, + sizeY = size * unitY, + pLeft = x - sizeX, + pTop = y + sizeY, + pRight = x + sizeX, + pBottom = y - sizeY; + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex2f( pLeft, pBottom ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex2f( pLeft, pTop ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex2f( pRight, pTop ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex2f( pRight, pBottom ); +} + +void DiscoAnalyzer::drawFullDot( float r, float g, float b, float a ) +{ + glBindTexture( GL_TEXTURE_2D, dotTexture ); + glEnable( GL_TEXTURE_2D ); + glColor4f( r, g, b, a ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + glDisable( GL_TEXTURE_2D ); +} + + +void DiscoAnalyzer::setTextureMatrix( float rot, float scale ) +{ + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + if( rot != 0.0 || scale != 0.0 ) + { + glTranslatef( 0.5f, 0.5f, 0.0f ); + glRotatef( rot, 0.0f, 0.0f, 1.0f ); + glScalef( scale, scale, 1.0f ); + glTranslatef( -0.5f, -0.5f, 0.0f ); + } + glMatrixMode( GL_MODELVIEW ); +} + +bool DiscoAnalyzer::loadTexture( QString fileName, GLuint& textureID ) +{ + //reset texture ID to the default EMPTY value + textureID = 0; + + //load image + QImage tmp; + if( !tmp.load( fileName ) ) + return false; + + //convert it to suitable format (flipped RGBA) + QImage texture = QGLWidget::convertToGLFormat( tmp ); + if( texture.isNull() ) + return false; + + //get texture number and bind loaded image to that texture + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() ); + return true; +} + + +void DiscoAnalyzer::freeTexture( GLuint & textureID ) +{ + if( textureID > 0 ) + glDeleteTextures( 1, &textureID ); + textureID = 0; +} + diff --git a/src/context/applets/analyzer/AnalyzerApplet.h b/src/context/applets/analyzer/DiscoAnalyzer.h similarity index 54% copy from src/context/applets/analyzer/AnalyzerApplet.h copy to src/context/applets/analyzer/DiscoAnalyzer.h index b47246f..6aa7343 100644 --- a/src/context/applets/analyzer/AnalyzerApplet.h +++ b/src/context/applets/analyzer/DiscoAnalyzer.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * Copyright (c) 2013 Mark Kretschmann <kretschm...@kde.org> * + * Copyright (c) 2004 Enrico Ros <eros....@email.it> * * * * 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 * @@ -14,25 +14,58 @@ * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ -#ifndef ANALYZER_APPLET_H -#define ANALYZER_APPLET_H +#ifndef DISCO_ANALYZER_H +#define DISCO_ANALYZER_H -#include "context/Applet.h" -#include "context/DataEngine.h" +#include "AnalyzerBase.h" -class AnalyzerApplet : public Context::Applet -{ - Q_OBJECT +class QPaintEvent; + +class DiscoAnalyzer : public Analyzer::Base3D +{ public: - AnalyzerApplet( QObject* parent, const QVariantList& args ); - virtual ~AnalyzerApplet(); + DiscoAnalyzer( QWidget * ); + ~DiscoAnalyzer(); + void analyze( const QVector<float>& ); + void paused(); -public slots: - virtual void init(); -}; +protected: + void initializeGL(); + void resizeGL( int w, int h ); + void paintGL(); + +private: + struct ShowProperties + { + bool paused; + double timeStamp; + double dT; + double pauseTimer; + float rotDegrees; + } showStruct; -AMAROK_EXPORT_APPLET( analyzer, AnalyzerApplet ) + struct FrameProperties + { + float energy; + float dEnergy; + float meanBand; + float rotDegrees; + bool silence; + } frame; + + GLuint dotTexture; + GLuint w1Texture; + GLuint w2Texture; + float unitX, unitY; + + void drawDot( float x, float y, float size ); + void drawFullDot( float r, float g, float b, float a ); + void setTextureMatrix( float rot, float scale ); + + bool loadTexture( QString file, GLuint& textureID ); + void freeTexture( GLuint& textureID ); +}; #endif _______________________________________________ Amarok-devel mailing list Amarok-devel@kde.org https://mail.kde.org/mailman/listinfo/amarok-devel