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 + -
显示快捷键?