1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-24 09:49:42 +01: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 "database/database.h"
#include <QtCore/QStateMachine>
#include <QtCore/QState>
#include <QtGui/QAction>
#include "sourcelist.h"
#include "database/databasecommand_socialaction.h"
#include "sourceplaylistinterface.h"
@ -82,7 +81,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr )
cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() );
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
return;
@ -117,7 +118,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr )
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();
}
void
LatchManager::unlatchRequest( const source_ptr& source )
{
@ -135,5 +139,19 @@ LatchManager::unlatchRequest( const source_ptr& source )
AudioEngine::instance()->stop();
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 unlatchRequest( const Tomahawk::source_ptr& source );
void catchUpRequest();
void latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime );
private slots:
void playlistChanged( Tomahawk::playlistinterface_ptr );

View File

@ -46,6 +46,10 @@ ActionCollection::initActions()
latchOff->setIcon( QIcon( RESPATH "images/headphones-off.png" ) );
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;
QAction *privacyToggle = new QAction( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ), this );
privacyToggle->setIcon( QIcon( RESPATH "images/private-listening.png" ) );

View File

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

View File

@ -228,12 +228,12 @@ AudioEngine::canGoNext()
m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards )
return false;
if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() &&
( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) )
if ( !m_currentTrack.isNull() && !m_playlist->hasNextItem() &&
( m_playlist->currentItem().isNull() || ( m_currentTrack->id() == m_playlist->currentItem()->id() ) ) )
{
//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
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;
}
@ -580,6 +580,14 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk:
void
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 )
return;

View File

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

View File

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

View File

@ -20,20 +20,26 @@
#include "source.h"
#include "pipeline.h"
#include "audio/audioengine.h"
#include "utils/logger.h"
using namespace Tomahawk;
SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source )
SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode )
: PlaylistInterface()
, m_source( source )
, m_currentItem( 0 )
, m_gotNextItem( false )
{
setLatchMode( latchMode );
if ( !m_source.isNull() )
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()
{
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
public:
SourcePlaylistInterface( Tomahawk::Source *source );
SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode = PlaylistInterface::RealTime );
virtual ~SourcePlaylistInterface();
QList<Tomahawk::query_ptr> tracks();
@ -64,6 +64,7 @@ public:
public slots:
virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {}
virtual void setShuffled( bool ) {}
virtual void audioPaused() { setLatchMode( PlaylistInterface::StayOnSong ); }
private slots:
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( unlatchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( unlatchRequest( Tomahawk::source_ptr ) ) );
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() ) );
}
@ -162,10 +163,14 @@ SourceTreeView::setupMenus()
{
if ( m_latchManager->isLatched( source ) )
{
m_latchMenu.addSeparator();
QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" );
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
SourceTreeView::renamePlaylist()

View File

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