1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-06 14:16:32 +02:00

Add the ability to listen along in real-time. Won't automatically switch

if you (currently, might need to tweak) have <= 6 seconds left of the
current track...after a while if you get further behind it'll
automatically correct.

Can toggle via source context menu right now, need to work in an icon.

If you pause the music, it turns off real-time if it's on.
This commit is contained in:
Jeff Mitchell
2012-01-12 17:55:33 -05:00
parent 7cd51a6108
commit 9242a7c942
11 changed files with 88 additions and 14 deletions

View File

@@ -22,8 +22,7 @@
#include "audio/audioengine.h" #include "audio/audioengine.h"
#include "database/database.h" #include "database/database.h"
#include <QtCore/QStateMachine> #include <QtGui/QAction>
#include <QtCore/QState>
#include "sourcelist.h" #include "sourcelist.h"
#include "database/databasecommand_socialaction.h" #include "database/databasecommand_socialaction.h"
#include "sourceplaylistinterface.h" #include "sourceplaylistinterface.h"
@@ -82,7 +81,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr )
cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Catch Up" ) ); QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" );
latchOnAction->setText( tr( "&Catch Up" ) );
latchOnAction->setIcon( QIcon() );
// If not, then keep waiting // If not, then keep waiting
return; return;
@@ -117,7 +118,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr )
m_state = NotLatched; m_state = NotLatched;
ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Listen Along" ) ); QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" );
latchOnAction->setText( tr( "&Listen Along" ) );
latchOnAction->setIcon( QIcon( RESPATH "images/headphones-sidebar.png" ) );
} }
@@ -128,6 +131,7 @@ LatchManager::catchUpRequest()
AudioEngine::instance()->next(); AudioEngine::instance()->next();
} }
void void
LatchManager::unlatchRequest( const source_ptr& source ) LatchManager::unlatchRequest( const source_ptr& source )
{ {
@@ -135,5 +139,19 @@ LatchManager::unlatchRequest( const source_ptr& source )
AudioEngine::instance()->stop(); AudioEngine::instance()->stop();
AudioEngine::instance()->setPlaylist( Tomahawk::playlistinterface_ptr() ); AudioEngine::instance()->setPlaylist( Tomahawk::playlistinterface_ptr() );
ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Listen Along" ) ); QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" );
latchOnAction->setText( tr( "&Listen Along" ) );
latchOnAction->setIcon( QIcon( RESPATH "images/headphones-sidebar.png" ) );
}
void
LatchManager::latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime )
{
if ( !isLatched( source ) )
return;
source->getPlaylistInterface()->setLatchMode( realtime ? Tomahawk::PlaylistInterface::RealTime : Tomahawk::PlaylistInterface::StayOnSong );
if ( realtime )
catchUpRequest();
} }

View File

@@ -43,6 +43,7 @@ public slots:
void latchRequest( const Tomahawk::source_ptr& source ); void latchRequest( const Tomahawk::source_ptr& source );
void unlatchRequest( const Tomahawk::source_ptr& source ); void unlatchRequest( const Tomahawk::source_ptr& source );
void catchUpRequest(); void catchUpRequest();
void latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime );
private slots: private slots:
void playlistChanged( Tomahawk::playlistinterface_ptr ); void playlistChanged( Tomahawk::playlistinterface_ptr );

View File

@@ -46,6 +46,10 @@ ActionCollection::initActions()
latchOff->setIcon( QIcon( RESPATH "images/headphones-off.png" ) ); latchOff->setIcon( QIcon( RESPATH "images/headphones-off.png" ) );
m_actionCollection[ "latchOff" ] = latchOff; m_actionCollection[ "latchOff" ] = latchOff;
QAction *realtimeFollowingAlong = new QAction( tr( "&Follow in real-time" ), this );
realtimeFollowingAlong->setCheckable( true );
m_actionCollection[ "realtimeFollowingAlong" ] = realtimeFollowingAlong;
bool isPublic = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening; bool isPublic = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening;
QAction *privacyToggle = new QAction( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ), this ); QAction *privacyToggle = new QAction( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ), this );
privacyToggle->setIcon( QIcon( RESPATH "images/private-listening.png" ) ); privacyToggle->setIcon( QIcon( RESPATH "images/private-listening.png" ) );

View File

@@ -21,7 +21,7 @@
#include "dllmacro.h" #include "dllmacro.h"
#include <QAction> #include <QtGui/QAction>
class DLLEXPORT ActionCollection : public QObject class DLLEXPORT ActionCollection : public QObject
{ {

View File

@@ -228,12 +228,12 @@ AudioEngine::canGoNext()
m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards ) m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards )
return false; return false;
if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() && if ( !m_currentTrack.isNull() && !m_playlist->hasNextItem() &&
( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) ) ( m_playlist->currentItem().isNull() || ( m_currentTrack->id() == m_playlist->currentItem()->id() ) ) )
{ {
//For instance, when doing a catch-up while listening along, but the person //For instance, when doing a catch-up while listening along, but the person
//you're following hasn't started a new track yet...don't do anything //you're following hasn't started a new track yet...don't do anything
tDebug( LOGEXTRA ) << Q_FUNC_INFO << "catch up"; tDebug( LOGEXTRA ) << Q_FUNC_INFO << "catch up, but same track or can't move on because don't have next track or it wasn't resolved";
return false; return false;
} }
@@ -580,6 +580,14 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk:
void void
AudioEngine::playlistNextTrackReady() AudioEngine::playlistNextTrackReady()
{ {
// If in real-time and you have a few seconds left, you're probably lagging -- finish it up
if ( m_playlist && m_playlist->latchMode() == PlaylistInterface::RealTime && ( m_waitingOnNewTrack || m_currentTrack.isNull() || m_currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) )
{
m_waitingOnNewTrack = false;
loadNextTrack();
return;
}
if ( !m_waitingOnNewTrack ) if ( !m_waitingOnNewTrack )
return; return;

View File

@@ -24,6 +24,7 @@ using namespace Tomahawk;
PlaylistInterface::PlaylistInterface () PlaylistInterface::PlaylistInterface ()
: QObject() : QObject()
, m_latchMode( StayOnSong )
{ {
qRegisterMetaType<Tomahawk::PlaylistInterface::RepeatMode>( "Tomahawk::PlaylistInterface::RepeatMode" ); qRegisterMetaType<Tomahawk::PlaylistInterface::RepeatMode>( "Tomahawk::PlaylistInterface::RepeatMode" );
} }

View File

@@ -39,6 +39,7 @@ public:
enum SeekRestrictions { NoSeekRestrictions, NoSeek }; enum SeekRestrictions { NoSeekRestrictions, NoSeek };
enum SkipRestrictions { NoSkipRestrictions, NoSkipForwards, NoSkipBackwards, NoSkip }; enum SkipRestrictions { NoSkipRestrictions, NoSkipForwards, NoSkipBackwards, NoSkip };
enum RetryMode { NoRetry, Retry }; enum RetryMode { NoRetry, Retry };
enum LatchMode { StayOnSong, RealTime };
explicit PlaylistInterface(); explicit PlaylistInterface();
virtual ~PlaylistInterface(); virtual ~PlaylistInterface();
@@ -55,14 +56,20 @@ public:
virtual Tomahawk::result_ptr siblingItem( int itemsAway ) = 0; virtual Tomahawk::result_ptr siblingItem( int itemsAway ) = 0;
virtual PlaylistInterface::RepeatMode repeatMode() const = 0; virtual PlaylistInterface::RepeatMode repeatMode() const = 0;
virtual bool shuffled() const = 0; virtual bool shuffled() const = 0;
virtual PlaylistInterface::ViewMode viewMode() const { return Unknown; } virtual PlaylistInterface::ViewMode viewMode() const { return Unknown; }
virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return NoSeekRestrictions; } virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return NoSeekRestrictions; }
virtual PlaylistInterface::SkipRestrictions skipRestrictions() const { return NoSkipRestrictions; } virtual PlaylistInterface::SkipRestrictions skipRestrictions() const { return NoSkipRestrictions; }
virtual PlaylistInterface::RetryMode retryMode() const { return NoRetry; } virtual PlaylistInterface::RetryMode retryMode() const { return NoRetry; }
virtual quint32 retryInterval() const { return 30000; } virtual quint32 retryInterval() const { return 30000; }
virtual PlaylistInterface::LatchMode latchMode() const { return m_latchMode; }
virtual void setLatchMode( PlaylistInterface::LatchMode latchMode ) { m_latchMode = latchMode; }
virtual QString filter() const { return m_filter; } virtual QString filter() const { return m_filter; }
virtual void setFilter( const QString& pattern ) { m_filter = pattern; } virtual void setFilter( const QString& pattern ) { m_filter = pattern; }
@@ -84,6 +91,9 @@ signals:
void sourceTrackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks );
void nextTrackReady(); void nextTrackReady();
protected:
LatchMode m_latchMode;
private: private:
Q_DISABLE_COPY( PlaylistInterface ) Q_DISABLE_COPY( PlaylistInterface )

View File

@@ -20,20 +20,26 @@
#include "source.h" #include "source.h"
#include "pipeline.h" #include "pipeline.h"
#include "audio/audioengine.h"
#include "utils/logger.h" #include "utils/logger.h"
using namespace Tomahawk; using namespace Tomahawk;
SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source ) SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode )
: PlaylistInterface() : PlaylistInterface()
, m_source( source ) , m_source( source )
, m_currentItem( 0 ) , m_currentItem( 0 )
, m_gotNextItem( false ) , m_gotNextItem( false )
{ {
setLatchMode( latchMode );
if ( !m_source.isNull() ) if ( !m_source.isNull() )
connect( m_source.data(), SIGNAL( playbackStarted( const Tomahawk::query_ptr& ) ), SLOT( onSourcePlaybackStarted( const Tomahawk::query_ptr& ) ) ); connect( m_source.data(), SIGNAL( playbackStarted( const Tomahawk::query_ptr& ) ), SLOT( onSourcePlaybackStarted( const Tomahawk::query_ptr& ) ) );
if ( AudioEngine::instance() )
connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( audioPaused() ) );
} }
@@ -95,7 +101,7 @@ QList<Tomahawk::query_ptr>
SourcePlaylistInterface::tracks() SourcePlaylistInterface::tracks()
{ {
QList<Tomahawk::query_ptr> tracks; QList<Tomahawk::query_ptr> tracks;
return tracks; // FIXME return tracks; // FIXME (with what?)
} }

View File

@@ -35,7 +35,7 @@ class DLLEXPORT SourcePlaylistInterface : public Tomahawk::PlaylistInterface
Q_OBJECT Q_OBJECT
public: public:
SourcePlaylistInterface( Tomahawk::Source *source ); SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode = PlaylistInterface::RealTime );
virtual ~SourcePlaylistInterface(); virtual ~SourcePlaylistInterface();
QList<Tomahawk::query_ptr> tracks(); QList<Tomahawk::query_ptr> tracks();
@@ -64,6 +64,7 @@ public:
public slots: public slots:
virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {}
virtual void setShuffled( bool ) {} virtual void setShuffled( bool ) {}
virtual void audioPaused() { setLatchMode( PlaylistInterface::StayOnSong ); }
private slots: private slots:
void onSourcePlaybackStarted( const Tomahawk::query_ptr& query ); void onSourcePlaybackStarted( const Tomahawk::query_ptr& query );

View File

@@ -118,6 +118,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
connect( this, SIGNAL( latchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( latchRequest( Tomahawk::source_ptr ) ) ); connect( this, SIGNAL( latchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( latchRequest( Tomahawk::source_ptr ) ) );
connect( this, SIGNAL( unlatchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( unlatchRequest( Tomahawk::source_ptr ) ) ); connect( this, SIGNAL( unlatchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( unlatchRequest( Tomahawk::source_ptr ) ) );
connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) ); connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) );
connect( this, SIGNAL( latchModeChangeRequest( Tomahawk::source_ptr, bool ) ), m_latchManager, SLOT( latchModeChangeRequest( Tomahawk::source_ptr, bool ) ) );
connect( ActionCollection::instance(), SIGNAL( privacyModeChanged() ), SLOT( repaint() ) ); connect( ActionCollection::instance(), SIGNAL( privacyModeChanged() ), SLOT( repaint() ) );
} }
@@ -162,10 +163,14 @@ SourceTreeView::setupMenus()
{ {
if ( m_latchManager->isLatched( source ) ) if ( m_latchManager->isLatched( source ) )
{ {
m_latchMenu.addSeparator();
QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" ); QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" );
m_latchMenu.addAction( latchOffAction ); m_latchMenu.addAction( latchOffAction );
connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ), Qt::QueuedConnection ); connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) );
m_latchMenu.addSeparator();
QAction *latchRealtimeAction = ActionCollection::instance()->getAction( "realtimeFollowingAlong" );
latchRealtimeAction->setChecked( source->getPlaylistInterface()->latchMode() == Tomahawk::PlaylistInterface::RealTime );
m_latchMenu.addAction( latchRealtimeAction );
connect( latchRealtimeAction, SIGNAL( toggled( bool ) ), SLOT( latchModeToggled( bool ) ) );
} }
} }
} }
@@ -416,6 +421,24 @@ SourceTreeView::latchOff( const Tomahawk::source_ptr& source )
} }
void
SourceTreeView::latchModeToggled( bool checked )
{
disconnect( this, SLOT( latchOff() ) );
qDebug() << Q_FUNC_INFO;
if ( !m_contextMenuIndex.isValid() )
return;
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
if( type != SourcesModel::Collection )
return;
const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
const source_ptr source = item->source();
emit latchModeChangeRequest( source, checked );
}
void void
SourceTreeView::renamePlaylist() SourceTreeView::renamePlaylist()

View File

@@ -58,6 +58,7 @@ signals:
void latchRequest( const Tomahawk::source_ptr& source ); void latchRequest( const Tomahawk::source_ptr& source );
void unlatchRequest( const Tomahawk::source_ptr& source ); void unlatchRequest( const Tomahawk::source_ptr& source );
void catchUpRequest(); void catchUpRequest();
void latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime );
private slots: private slots:
void onItemExpanded( const QModelIndex& idx ); void onItemExpanded( const QModelIndex& idx );
@@ -75,6 +76,7 @@ private slots:
void latchOff(); void latchOff();
void latchOnOrCatchUp( const Tomahawk::source_ptr& source ); void latchOnOrCatchUp( const Tomahawk::source_ptr& source );
void latchOff( const Tomahawk::source_ptr& source ); void latchOff( const Tomahawk::source_ptr& source );
void latchModeToggled( bool checked );
void onCustomContextMenu( const QPoint& pos ); void onCustomContextMenu( const QPoint& pos );