moodbar.cpp
来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 1,387 行 · 第 1/4 页
CPP
1,387 行
// PlaylistItem knows what do display#define JOB_PENDING(state) ((state)==JobQueued||(state)==JobRunning)// The passed MetaBundle _must_ be non-NULL, and the pointer must be valid// as long as this instance is alive. The Moodbar is only meant to be a// member of a MetaBundle, in other words.Moodbar::Moodbar( MetaBundle *mb ) : QObject ( ) , m_bundle ( mb ) , m_hueSort ( 0 ) , m_state ( Unloaded ){}// If we have any pending jobs, de-queue them. The use case I// have in mind is if the user has the moodbar column displayed// and adds all his/her tracks to the playlist, then deletes// them again.Moodbar::~Moodbar( void ){ if( JOB_PENDING( m_state ) ) MoodServer::instance()->deQueueJob( m_url );}// MetaBundle's are often assigned using operator=, so so are we.Moodbar&Moodbar::operator=( const Moodbar &mood ){ // Need to check this before locking both! if( &mood == this ) return *this; m_mutex.lock(); mood.m_mutex.lock(); State oldState = m_state; KURL oldURL = m_url; m_data = mood.m_data; m_pixmap = mood.m_pixmap; m_state = mood.m_state; m_url = mood.m_url; // DO NOT overwrite m_bundle! That should never change. // Signal connections and job queues are part of our "state", // so those should be updated too. if( JOB_PENDING( m_state ) && !JOB_PENDING( oldState ) ) { connect( MoodServer::instance(), SIGNAL( jobEvent( KURL, int ) ), SLOT( slotJobEvent( KURL, int ) ) ); // Increase the refcount for this job. Use mood.m_bundle // since that one's already initialized. MoodServer::instance()->queueJob( mood.m_bundle ); } // If we had a job pending, de-queue it if( !JOB_PENDING( m_state ) && JOB_PENDING( oldState ) ) { MoodServer::instance()->disconnect( this, SLOT( slotJobEvent( KURL, int ) ) ); MoodServer::instance()->deQueueJob( oldURL ); } mood.m_mutex.unlock(); m_mutex.unlock(); return *this;}// Reset the moodbar to its Unloaded state. This is useful when// the configuration is changed, and all the moodbars need to be// reloaded.voidMoodbar::reset( void ){ m_mutex.lock(); debug() << "Resetting moodbar: " << m_bundle->url().path() << endl; if( JOB_PENDING( m_state ) ) { MoodServer::instance()->disconnect( this, SLOT( slotJobEvent( KURL, int ) ) ); MoodServer::instance()->deQueueJob( m_url ); } m_data.clear(); m_pixmap = QPixmap(); m_url = KURL(); m_hueSort = 0; m_state = Unloaded; m_mutex.unlock();}// Make a copy of all of our implicitly shared datavoidMoodbar::detach( void ){ m_mutex.lock(); m_data = QDeepCopy<ColorList>(m_data); m_pixmap.detach(); // Apparently this is the wrong hack -- don't detach urls //QString url( QDeepCopy<QString>( m_url.url() ) ); //m_url = KURL::fromPathOrURL( url ); m_mutex.unlock();}// If possible, try to open the bundle's .mood file. When this method// returns true, this instance must be able to draw(). This may// change the state to CantLoad, but usually leaves the state// untouched.boolMoodbar::dataExists( void ){ // Put this first for efficiency if( m_state == Loaded ) return true; // Should we bother checking for the file? if( m_state == CantLoad || JOB_PENDING( m_state ) || m_state == JobFailed || !canHaveMood() ) return false; m_mutex.lock(); bool res = readFile(); m_mutex.unlock(); return res;}// If m_bundle is not a local file or for some other reason cannot// have mood data, return false, and set the state to CantLoad to// save future checks. Note that MoodServer::m_moodbarBroken == true// does not mean we can't have a mood file; it just means that we// can't generate new ones.boolMoodbar::canHaveMood( void ){ if( m_state == CantLoad ) return false; // Don't try to analyze it if we can't even determine it has a length // If for some reason we can't determine a file name, give up // If the moodbar is disabled, set to CantLoad -- if the user re-enables // the moodbar, we'll be reset() anyway. if( !AmarokConfig::showMoodbar() || !m_bundle->url().isLocalFile() || !m_bundle->length() || moodFilename( m_bundle->url() ).isEmpty() ) { m_state = CantLoad; return false; } return true;}// Ask MoodServer to queue an analyzer job for us if necessary. This// method will only do something the first time it's called, as it's// guaranteed to change the state from Unloaded.voidMoodbar::load( void ){ if( m_state != Unloaded ) return; m_mutex.lock(); if( !canHaveMood() ) { // State is now CantLoad m_mutex.unlock(); return; } if( readFile() ) { // State is now Loaded m_mutex.unlock(); return; } if( MoodServer::instance()->moodbarBroken() ) { m_state = JobFailed; m_mutex.unlock(); return; } // Ok no more excuses, we have to queue a job connect( MoodServer::instance(), SIGNAL( jobEvent( KURL, int ) ), SLOT( slotJobEvent( KURL, int ) ) ); bool isRunning = MoodServer::instance()->queueJob( m_bundle ); m_state = isRunning ? JobRunning : JobQueued; m_url = m_bundle->url(); // Use this URL for MoodServer::deQueueJob m_mutex.unlock();}// This is called by MoodServer when our moodbar analyzer job starts// or finishes. It may change the state from JobQueued / JobRunning// to JobRunning, Loaded, or JobFailed. It may emit a jobEvent()voidMoodbar::slotJobEvent( KURL url, int newState ){ // Is this job for us? if( !JOB_PENDING( m_state ) || url != m_bundle->url() ) return; bool success = ( newState == JobStateSucceeded ); // We don't really care about this, but our listeners might if( newState == JobStateRunning ) { m_state = JobRunning; goto out; } m_mutex.lock(); // Disconnect the signal for efficiency's sake MoodServer::instance()->disconnect( this, SLOT( slotJobEvent( KURL, int ) ) ); if( !success ) { m_state = JobFailed; m_mutex.unlock(); goto out; } if( readFile() ) { // m_state is now Loaded m_mutex.unlock(); goto out; } // If we get here it means the analyzer job went wrong, but // somehow the MoodServer didn't know about it debug() << "WARNING: Failed to open file " << moodFilename( m_bundle->url() ) << " -- something is very wrong" << endl; m_state = JobFailed; m_mutex.unlock(); out: emit jobEvent( newState ); // This is a cheat for PlaylistItem so it doesn't have to // use signals m_bundle->moodbarJobEvent( newState );}// Draw the moodbar onto a pixmap of the given dimensions and return// it. This is mostly Gav's original code, cut and pasted from// various places. This will not change the state.QPixmapMoodbar::draw( int width, int height ){ if( m_state != Loaded || !AmarokConfig::showMoodbar() ) // Naughty caller! return QPixmap(); m_mutex.lock(); // Do we have to repaint, or can we use the cache? if( m_pixmap.width() == width && m_pixmap.height() == height ) { m_mutex.unlock(); return m_pixmap; } m_pixmap = QPixmap( width, height ); QPainter paint( &m_pixmap ); // First average the moodbar samples that will go into each // vertical bar on the screen. if( m_data.size() == 0 ) // Play it safe -- see below return QPixmap(); ColorList screenColors; QColor bar; float r, g, b; int h, s, v; for( int i = 0; i < width; i++ ) { r = 0.f; g = 0.f; b = 0.f; // m_data.size() needs to be at least 1 for this not to crash! uint start = i * m_data.size() / width; uint end = (i + 1) * m_data.size() / width; if( start == end ) end = start + 1; for( uint j = start; j < end; j++ ) { r += m_data[j].red(); g += m_data[j].green(); b += m_data[j].blue(); } uint n = end - start; bar = QColor( int( r / float( n ) ), int( g / float( n ) ), int( b / float( n ) ), QColor::Rgb ); /* Snap to the HSV values for later */ bar.getHsv(&h, &s, &v); bar.setHsv(h, s, v); screenColors.push_back( bar ); } // Paint the bars. This is Gav's painting code -- it breaks up the // monotony of solid-color vertical bars by playing with the saturation // and value. for( int x = 0; x < width; x++ ) { screenColors[x].getHsv( &h, &s, &v ); for( int y = 0; y <= height / 2; y++ ) { float coeff = float(y) / float(height / 2); float coeff2 = 1.f - ((1.f - coeff) * (1.f - coeff)); coeff = 1.f - (1.f - coeff) / 2.f; coeff2 = 1.f - (1.f - coeff2) / 2.f; paint.setPen( QColor( h, CLAMP( 0, int( float( s ) * coeff ), 255 ), CLAMP( 0, int( 255.f - (255.f - float( v )) * coeff2), 255 ),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?