enginecontroller.cpp

来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 796 行 · 第 1/2 页

CPP
796
字号
/*************************************************************************** *   Copyright (C) 2004 Frederik Holljen <fh@ez.no>                        * *             (C) 2004,5 Max Howell <max.howell@methylblue.com>           * *             (C) 2004,5 Mark Kretschmann                                 * *             (C) 2006 Ian Monroe                                         * *                                                                         * *   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.                                   * *                                                                         * ***************************************************************************/#define DEBUG_PREFIX "controller"#include "amarok.h"#include "amarokconfig.h"#include "debug.h"#include "enginebase.h"#include "enginecontroller.h"#include "lastfm.h"#include "mediabrowser.h"#include "playlist.h"#include "playlistloader.h"#include "pluginmanager.h"#include "statusbar.h"#include <qfile.h>#include <qobjectlist.h>#include <qtimer.h>#include <kapplication.h>#include <kfileitem.h>#include <kio/global.h>#include <kio/job.h>#include <kmessagebox.h>#include <krun.h>#include <cstdlib>EngineController::ExtensionCache EngineController::s_extensionCache;EngineController*EngineController::instance(){    //will only be instantiated the first time this function is called    //will work with the inline directive    static EngineController Instance;    return &Instance;}EngineController::EngineController()        : m_engine( 0 )        , m_voidEngine( 0 )        , m_delayTime( 0 )        , m_muteVolume( 0 )        , m_xFadeThisTrack( false )        , m_timer( new QTimer( this ) )        , m_playFailureCount( 0 )        , m_lastFm( false )        , m_positionOffset( 0 )        , m_lastPositionOffset( 0 ){    m_voidEngine = m_engine = loadEngine( "void-engine" );    connect( m_timer, SIGNAL( timeout() ), SLOT( slotMainTimer() ) );}EngineController::~EngineController(){    DEBUG_FUNC_INFO //we like to know when singletons are destroyed}//////////////////////////////////////////////////////////////////////////////////////////// PUBLIC//////////////////////////////////////////////////////////////////////////////////////////EngineBase*EngineController::loadEngine() //static{    /// always returns a valid pointer to EngineBase    DEBUG_BLOCK    //TODO remember song position, and resume playback    // new engine, new ext cache required    extensionCache().clear();    if( m_engine != m_voidEngine ) {        EngineBase *oldEngine = m_engine;        // we assign this first for thread-safety,        // EngineController::engine() must always return an engine!        m_engine = m_voidEngine;        // we unload the old engine first because there are a number of        // bugs associated with keeping one engine loaded while loading        // another, eg xine-engine can't init(), and aRts-engine crashes        PluginManager::unload( oldEngine );        // the engine is not required to do this when we unload it but        // we need to do it to ensure Amarok looks correct.        // We don't do this for the void-engine because that        // means Amarok sets all components to empty on startup, which is        // their responsibility.        slotStateChanged( Engine::Empty );    }    m_engine = loadEngine( AmarokConfig::soundSystem() );    const QString engineName = PluginManager::getService( m_engine )->property( "X-KDE-Amarok-name" ).toString();    if( !AmarokConfig::soundSystem().isEmpty() && engineName != AmarokConfig::soundSystem() ) {        //AmarokConfig::soundSystem() is empty on the first-ever-run        Amarok::StatusBar::instance()->longMessage( i18n(                "Sorry, the '%1' could not be loaded, instead we have loaded the '%2'." )                        .arg( AmarokConfig::soundSystem() )                        .arg( engineName ),                KDE::StatusBar::Sorry );        AmarokConfig::setSoundSystem( engineName );    }    // Important: Make sure soundSystem is not empty    if( AmarokConfig::soundSystem().isEmpty() )        AmarokConfig::setSoundSystem( engineName );    return m_engine;}#include <qvaluevector.h>EngineBase*EngineController::loadEngine( const QString &engineName ){    /// always returns a valid plugin (exits if it can't get one)    DEBUG_BLOCK    QString query = "[X-KDE-Amarok-plugintype] == 'engine' and [X-KDE-Amarok-name] != '%1'";    KTrader::OfferList offers = PluginManager::query( query.arg( engineName ) );    // sort by rank, QValueList::operator[] is O(n), so this is quite inefficient    #define rank( x ) (x)->property( "X-KDE-Amarok-rank" ).toInt()    for( int n = offers.count()-1, i = 0; i < n; i++ )        for( int j = n; j > i; j-- )            if( rank( offers[j] ) > rank( offers[j-1] ) )                qSwap( offers[j], offers[j-1] );    #undef rank    // this is the actual engine we want    query = "[X-KDE-Amarok-plugintype] == 'engine' and [X-KDE-Amarok-name] == '%1'";    offers = PluginManager::query( query.arg( engineName ) ) + offers;    foreachType( KTrader::OfferList, offers ) {        Amarok::Plugin *plugin = PluginManager::createFromService( *it );        if( plugin ) {            QObject *bar = Amarok::StatusBar::instance();            EngineBase *engine = static_cast<EngineBase*>( plugin );            connect( engine, SIGNAL(stateChanged( Engine::State )),                       this,   SLOT(slotStateChanged( Engine::State )) );            connect( engine, SIGNAL(trackEnded()),                       this,   SLOT(slotTrackEnded()) );            if( bar )            {                connect( engine, SIGNAL(statusText( const QString& )),                            bar,   SLOT(shortMessage( const QString& )) );                connect( engine, SIGNAL(infoMessage( const QString& )),                            bar,   SLOT(longMessage( const QString& )) );            }            connect( engine, SIGNAL(metaData( const Engine::SimpleMetaBundle& )),                       this,   SLOT(slotEngineMetaData( const Engine::SimpleMetaBundle& )) );            connect( engine, SIGNAL(showConfigDialog( const QCString& )),                       kapp,   SLOT(slotConfigAmarok( const QCString& )) );            if( engine->init() )                return engine;            else                warning() << "Could not init() an engine\n";        }    }    KRun::runCommand( "kbuildsycoca" );    KMessageBox::error( 0, i18n(            "<p>Amarok could not find any sound-engine plugins. "            "Amarok is now updating the KDE configuration database. Please wait a couple of minutes, then restart Amarok.</p>"            "<p>If this does not help, "            "it is likely that Amarok is installed under the wrong prefix, please fix your installation using:<pre>"            "$ cd /path/to/amarok/source-code/<br>"            "$ su -c \"make uninstall\"<br>"            "$ ./configure --prefix=`kde-config --prefix` && su -c \"make install\"<br>"            "$ kbuildsycoca<br>"            "$ amarok</pre>"            "More information can be found in the README file. For further assistance join us at #amarok on irc.freenode.net.</p>" ) );    // don't use QApplication::exit, as the eventloop may not have started yet    std::exit( EXIT_SUCCESS );    // Not executed, just here to prevent compiler warning    return 0;}bool EngineController::canDecode( const KURL &url ) //static{   //NOTE this function must be thread-safe    //TODO a KFileItem version? <- presumably so we can mimetype check    const QString fileName = url.fileName();    const QString ext = Amarok::extension( fileName );    if ( PlaylistFile::isPlaylistFile( fileName ) ) return false;    // Ignore protocols "fetchcover" and "musicbrainz", they're not local but we don't really want them in the playlist :)    if ( url.protocol() == "fetchcover" || url.protocol() == "musicbrainz" ) return false;    // Accept non-local files, since we can't test them for validity at this point    // TODO actually, only accept unconditionally http stuff    // TODO this actually makes things like "Blarrghgjhjh:!!!" automatically get inserted    // into the playlist    // TODO remove for Amarok 1.3 and above silly checks, instead check for http type servers    if ( !url.isLocalFile() ) return true;    // If extension is already in the cache, return cache result    if ( extensionCache().contains( ext ) )        return s_extensionCache[ext];    // If file has 0 bytes, ignore it and return false, not to infect the cache with corrupt files.    // TODO also ignore files that are too small?    KFileItem f( KFileItem::Unknown, KFileItem::Unknown, url, false );    if ( !f.size() )        return false;    const bool valid = engine()->canDecode( url );    if( engine() != EngineController::instance()->m_voidEngine )    {        //we special case this as otherwise users hate us        if ( !valid && ext.lower() == "mp3"){            QCustomEvent * e = new QCustomEvent( 2000 );            QApplication::postEvent( Amarok::StatusBar::instance(), e );	}        // Cache this result for the next lookup        if ( !ext.isEmpty() )            extensionCache().insert( ext, valid );    }    return valid;}void EngineController::unplayableNotification() {    if( !installDistroCodec(AmarokConfig::soundSystem()))        Amarok::StatusBar::instance()->longMessageThreadSafe(                  i18n( "<p>The %1 claims it <b>cannot</b> play MP3 files."                        "<p>You may want to choose a different engine from the <i>Configure Dialog</i>, or examine "                        "the installation of the multimedia-framework that the current engine uses. "                        "<p>You may find useful information in the <i>FAQ</i> section of the <i>Amarok HandBook</i>." )                    .arg( AmarokConfig::soundSystem() ), KDE::StatusBar::Error );}bool EngineController::installDistroCodec( const QString& engine /*Filetype type*/){    KService::Ptr service = KTrader::self()->query( "Amarok/CodecInstall"        , QString("[X-KDE-Amarok-codec] == 'mp3' and [X-KDE-Amarok-engine] == '%1'").arg(engine) ).first();    if( service )    {        QString installScript = service->exec();        if( !installScript.isNull() ) //just a sanity check        {            KGuiItem installButton("Install MP3 Support");            if(KMessageBox::questionYesNo(PlaylistWindow::self()            , i18n("Amarok currently cannot play MP3 files.")            , i18n( "No MP3 Support" )            , installButton            , KStdGuiItem::no()            , "codecInstallWarning" ) == KMessageBox::Yes )            {                    KRun::runCommand(installScript);                    return true;            }        }    }return false;}void EngineController::restoreSession(){    //here we restore the session    //however, do note, this is always done, KDE session management is not involved    if( !AmarokConfig::resumeTrack().isEmpty() )    {        const KURL url = AmarokConfig::resumeTrack();        play( MetaBundle( url ), AmarokConfig::resumeTime() );    }}void EngineController::endSession(){    //only update song stats, when we're not going to resume it    if ( !AmarokConfig::resumePlayback() )    {        trackEnded( trackPosition(), m_bundle.length() * 1000, "quit" );    }    PluginManager::unload( m_voidEngine );    m_voidEngine = 0;}//////////////////////////////////////////////////////////////////////////////////////////// PUBLIC SLOTS//////////////////////////////////////////////////////////////////////////////////////////void EngineController::previous() //SLOT{    emit orderPrevious();}void EngineController::next( bool forceNext ) //SLOT{    m_previousUrl = m_bundle.url();    m_isTiming = false;    emit orderNext(forceNext);}void EngineController::play() //SLOT{    if ( m_engine->state() == Engine::Paused )    {        m_engine->unpause();    }    else emit orderCurrent();}void EngineController::play( const MetaBundle &bundle, uint offset ){    DEBUG_BLOCK    KURL url = bundle.url();    // don't destroy connection if we need to change station    if( url.protocol() != "lastfm" && LastFm::Controller::instance()->isPlaying() )    {        m_engine->stop();        LastFm::Controller::instance()->playbackStopped();    }    m_lastFm = false;    //Holds the time since we started trying to play non-existent files    //so we know when to abort    static QTime failure_time;    if ( !m_playFailureCount )        failure_time.start();    debug() << "Loading URL: " << url.url() << endl;    m_lastMetadata.clear();    //TODO bummer why'd I do it this way? it should _not_ be in play!    //let Amarok know that the previous track is no longer playing    if ( m_timer->isActive() )        trackEnded( trackPosition(), m_bundle.length() * 1000, "change" );    if ( url.isLocalFile() ) {        // does the file really exist? the playlist entry might be old        if ( ! QFile::exists( url.path()) ) {            //debug() << "  file >" << url.path() << "< does not exist!" << endl;            Amarok::StatusBar::instance()->shortMessage( i18n("Local file does not exist.") );            goto some_kind_of_failure;        }    }    else    {        if( url.protocol() == "cdda" )            Amarok::StatusBar::instance()->shortMessage( i18n("Starting CD Audio track...") );        else            Amarok::StatusBar::instance()->shortMessage( i18n("Connecting to stream source...") );        debug() << "Connecting to protocol: " << url.protocol() << endl;    }    // WebDAV protocol is HTTP with extensions (and the "webdav" scheme    // is a KDE-ism anyway). Most engines cope with HTTP streaming, but    // not through KIO, so they don't support KDE-isms.    if ( url.protocol() == "webdav" )        url.setProtocol( "http" );    else if ( url.protocol() == "webdavs" )

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?