/*************************************************************************** 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 //interpolate() #include //event() #include #include #include // 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 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; #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( &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&) //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; } }