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