/*************************************************************************** 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 //interpolate() #include "enginecontroller.h" #include //event() // 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 TQt function insetead of m_background // make the linker happy only for gcc < 4.0 #if !( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 0 ) ) template class Analyzer::Base; #endif template Analyzer::Base::Base( TQWidget *parent, uint timeout, uint scopeSize ) : W( parent ) , m_timeout( timeout ) , m_fht( new FHT(scopeSize) ) {} template bool Analyzer::Base::event( TQEvent *e ) { switch( e->type() ) { /* case TQEvent::Paint: if( !canvas()->isNull() ) bitBlt( this, 0, 0, canvas() ); return true; //no propagate event*/ case TQEvent::Hide: m_timer.stop(); break; case TQEvent::Show: m_timer.start( timeout() ); break; default: break; } return TQWidget::event( e ); } template 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; } template void Analyzer::Base::drawFrame() { EngineBase *engine = EngineController::engine(); switch( engine->state() ) { case Engine::Playing: { const Engine::Scope &thescope = engine->scope(); static Analyzer::Scope scope( 512 ); 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 ) { scope[x] = double(thescope[i] + thescope[i+1]) / (2*(1<<15)); i += 2; } transform( scope ); analyze( scope ); scope.resize( m_fht->size() ); break; } case Engine::Paused: paused(); break; default: demo(); } } template 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; } template 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; } template void Analyzer::Base::paused() //virtual {} template void Analyzer::Base::demo() //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( s ); } else analyze( Scope( 32, 0 ) ); ++t; } Analyzer::Base2D::Base2D( TQWidget *parent, uint timeout, uint scopeSize ) : Base( parent, timeout, scopeSize ) { setWFlags( TQt::WNoAutoErase ); //no flicker connect( &m_timer, TQT_SIGNAL( timeout() ), TQT_SLOT( draw() ) ); } void Analyzer::Base2D::polish() { //TODO is there much point in this anymore? //we use polish for initialzing (instead of ctor) //because we need to know the widget's final size TQWidget::polish(); init(); //virtual } void Analyzer::Base2D::resizeEvent( TQResizeEvent *e ) { m_background.resize( size() ); m_canvas.resize( size() ); m_background.fill( backgroundColor() ); eraseCanvas(); //this is necessary TQWidget::resizeEvent( e ); } void Analyzer::Base2D::paletteChange( const TQPalette& ) { m_background.fill( backgroundColor() ); eraseCanvas(); } #ifdef HAVE_TQGLWIDGET Analyzer::Base3D::Base3D( TQWidget *parent, uint timeout, uint scopeSize ) : Base( parent, timeout, scopeSize ) { connect( &m_timer, TQT_SIGNAL( timeout() ), TQT_SLOT( draw() ) ); } #endif 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; } } #include "analyzerbase.moc"