[banshee/gapless-ng: 413/836] Merge gapless work to trunk.



commit 2966db6bcad05fd2a4d43366e3cd34b6d43aab5c
Merge: 5909a8e aab7faf
Author: Christopher James Halse Rogers <raof ubuntu com>
Date:   Mon Nov 16 11:22:42 2009 +1100

    Merge gapless work to trunk.
    
    Fix text conflicts caused by the trailing-whitespace fixes

 libbanshee/banshee-player-pipeline.c               |  107 ++++++++++++++++----
 libbanshee/banshee-player-private.h                |   14 +++
 libbanshee/banshee-player-video.c                  |   16 ++--
 libbanshee/banshee-player.c                        |   40 +++++++
 .../Banshee.GStreamer/PlayerEngine.cs              |  104 +++++++++++++++++++
 .../Banshee.MediaEngine/PlayerEngine.cs            |   50 +++++++++
 .../Banshee.MediaEngine/PlayerEngineService.cs     |   85 ++++++++++++----
 .../Banshee.MediaEngine/PlayerEvent.cs             |    3 +-
 .../IBasicPlaybackController.cs                    |    2 +-
 .../PlaybackControllerService.cs                   |   84 +++++++++-------
 .../Banshee.InternetRadio/InternetRadioSource.cs   |   14 +++-
 .../Banshee.Lastfm.Radio/StationSource.cs          |   23 +++--
 .../Banshee.PlayQueue/PlayQueueSource.cs           |   15 ++-
 13 files changed, 461 insertions(+), 96 deletions(-)
---
diff --cc src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
index 3193187,788a3e2..589b9b1
--- a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
@@@ -79,11 -82,16 +82,16 @@@ namespace Banshee.GStreame
          private BansheePlayerVisDataCallback vis_data_callback;
          private VideoPipelineSetupHandler video_pipeline_setup_callback;
          private GstTaggerTagFoundCallback tag_found_callback;
+         private BansheePlayerNextTrackStartingCallback next_track_starting_callback;
+         private BansheePlayerAboutToFinishCallback about_to_finish_callback;
 -        
 +
          private bool buffering_finished;
          private int pending_volume = -1;
          private bool xid_is_set = false;
  
+         private bool gapless_enabled;
+         private EventWaitHandle next_track_set;
 -        
++
          private event VisualizationDataHandler data_available = null;
          public event VisualizationDataHandler DataAvailable {
              add {
@@@ -142,9 -152,12 +152,12 @@@
              bp_set_state_changed_callback (handle, state_changed_callback);
              bp_set_buffering_callback (handle, buffering_callback);
              bp_set_tag_found_callback (handle, tag_found_callback);
+             bp_set_next_track_starting_callback (handle, next_track_starting_callback);
              bp_set_video_pipeline_setup_callback (handle, video_pipeline_setup_callback);
+ 
+             next_track_set = new EventWaitHandle (false, EventResetMode.AutoReset);
          }
 -        
 +
          protected override void Initialize ()
          {
              if (!bp_initialize_pipeline (handle)) {
@@@ -158,11 -171,12 +171,12 @@@
              if (pending_volume >= 0) {
                  Volume = (ushort)pending_volume;
              }
 -            
 +
              InstallPreferences ();
              ReplayGainEnabled = ReplayGainEnabledSchema.Get ();
+             GaplessEnabled = GaplessEnabledSchema.Get ();
          }
 -        
 +
          public override void Dispose ()
          {
              UninstallPreferences ();
@@@ -210,6 -224,23 +224,23 @@@
              bp_pause (handle);
          }
  
+         public override void SetNextTrackUri (SafeUri uri)
+         {
+             // If there isn't a next track for us, release the block on the about-to-finish callback.
+             if (uri == null) {
+                 next_track_set.Set ();
+                 bp_set_next_track (handle, IntPtr.Zero);
+                 return;
+             }            
+             IntPtr uri_ptr = GLib.Marshaller.StringToPtrGStrdup (uri.AbsoluteUri);
+             try {
+                 bp_set_next_track (handle, uri_ptr);
+             } finally {
+                 GLib.Marshaller.Free (uri_ptr);
+             }
+             next_track_set.Set ();
+         }
 -        
++
          public override void VideoExpose (IntPtr window, bool direct)
          {
              bp_video_window_expose (handle, window, direct);
@@@ -230,8 -261,39 +261,39 @@@
          {
              Close (false);
              OnEventChanged (PlayerEvent.EndOfStream);
+             if (!GaplessEnabled) {
+                 OnEventChanged (PlayerEvent.RequestNextTrack);
+             }
+         }
+ 
+         private void OnNextTrackStarting (IntPtr player)
+         {
+             if (GaplessEnabled) {
+                 OnEventChanged (PlayerEvent.EndOfStream);
+                 OnEventChanged (PlayerEvent.StartOfStream);
+             }
+         }
+ 
+         private void OnAboutToFinish (IntPtr player)
+         {
+             // This is needed to make Shuffle-by-* work.
+             // Shuffle-by-* uses the LastPlayed field to determine what track in the grouping to play next.
+             // Therefore, we need to update this before requesting the next track.
+             //
+             // This will be overridden by IncrementLastPlayed () called by 
+             // PlaybackControllerService's EndOfStream handler.
+             CurrentTrack.LastPlayed = DateTime.Now;
+             CurrentTrack.Save ();
+             
+             OnEventChanged (PlayerEvent.RequestNextTrack);
+             // Gapless playback with Playbin2 requires that the about-to-finish callback does not return until
+             // the next uri has been set.  Block here for a second or until the RequestNextTrack event has 
+             // finished triggering.
+             if (!next_track_set.WaitOne (1000, false)) {
+                 Log.Debug ("[Gapless] Timed out while waiting for next_track_set to be raised");
+             }
 -        }       
 -        
 +        }
 +
          private void OnIterate (IntPtr player)
          {
              OnEventChanged (PlayerEvent.Iterate);
@@@ -550,37 -627,52 +628,52 @@@
              if (service == null) {
                  return;
              }
 -            
 -            replaygain_preference = service["general"]["misc"].Add (new SchemaPreference<bool> (ReplayGainEnabledSchema, 
 +
 +            replaygain_preference = service["general"]["misc"].Add (new SchemaPreference<bool> (ReplayGainEnabledSchema,
                  Catalog.GetString ("_Enable ReplayGain correction"),
 -                Catalog.GetString ("For tracks that have ReplayGain data, automatically scale (normalize) playback volume."),
 +                Catalog.GetString ("For tracks that have ReplayGain data, automatically scale (normalize) playback volume"),
                  delegate { ReplayGainEnabled = ReplayGainEnabledSchema.Get (); }
              ));
+             gapless_preference = service["general"]["misc"].Add (new SchemaPreference<bool> (GaplessEnabledSchema,
+                 Catalog.GetString ("Enable _gapless playback"),
+                 Catalog.GetString ("Eliminate the small playback gap on track change.  Useful for concept albums and classical music."),
+                 delegate { GaplessEnabled = GaplessEnabledSchema.Get (); }
+             ));                            
          }
 -        
 +
          private void UninstallPreferences ()
          {
              PreferenceService service = ServiceManager.Get<PreferenceService> ();
              if (service == null) {
                  return;
              }
 -            
 +
              service["general"]["misc"].Remove (replaygain_preference);
+             service["general"]["misc"].Remove (gapless_preference);
              replaygain_preference = null;
+             gapless_preference = null;
          }
 -        
 +
          public static readonly SchemaEntry<bool> ReplayGainEnabledSchema = new SchemaEntry<bool> (
 -            "player_engine", "replay_gain_enabled", 
 +            "player_engine", "replay_gain_enabled",
              false,
              "Enable ReplayGain",
              "If ReplayGain data is present on tracks when playing, allow volume scaling"
          );
  
+         public static readonly SchemaEntry<bool> GaplessEnabledSchema = new SchemaEntry<bool> (
+             "player_engine", "gapless_playback_enabled",
+             true,
+             "Enable gapless playback",
+             "Eliminate the small playback gap on track change.  Useful for concept albums & classical music.  EXPERIMENTAL"
+         );
+                                                                                               
+ 
  #endregion
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern IntPtr bp_new ();
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern bool bp_initialize_pipeline (HandleRef player);
  
@@@ -617,35 -709,46 +710,46 @@@
              GstTaggerTagFoundCallback cb);
  
          [DllImport ("libbanshee.dll")]
+         private static extern void bp_set_next_track_starting_callback (HandleRef player,
+             BansheePlayerNextTrackStartingCallback cb);
+ 
+         [DllImport ("libbanshee.dll")]
+         private static extern void bp_set_about_to_finish_callback (HandleRef player,
+             BansheePlayerAboutToFinishCallback cb);
 -        
++
+         [DllImport ("libbanshee.dll")]
          private static extern bool bp_open (HandleRef player, IntPtr uri);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern void bp_stop (HandleRef player, bool nullstate);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern void bp_pause (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern void bp_play (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
+         private static extern bool bp_set_next_track (HandleRef player, IntPtr uri);
+ 
+         [DllImport ("libbanshee.dll")]
          private static extern void bp_set_volume (HandleRef player, double volume);
 -        
 +
          [DllImport("libbanshee.dll")]
          private static extern double bp_get_volume (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern bool bp_can_seek (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern bool bp_set_position (HandleRef player, ulong time_ms);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern ulong bp_get_position (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern ulong bp_get_duration (HandleRef player);
 -        
 +
          [DllImport ("libbanshee.dll")]
          private static extern bool bp_get_pipeline_elements (HandleRef player, out IntPtr playbin,
              out IntPtr audiobin, out IntPtr audiotee);
diff --cc src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
index b074d2b,435b594..e5f31b0
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
@@@ -42,17 -42,19 +42,19 @@@ namespace Banshee.MediaEngin
      {
          public const int VolumeDelta = 10;
          public const int SkipDelta = 10;
 -    
 +
          public event PlayerEventHandler EventChanged;
 -        
 +
          private TrackInfo current_track;
          private SafeUri current_uri;
+         private TrackInfo pending_track;
+         private SafeUri pending_uri;
          private PlayerState current_state = PlayerState.NotReady;
          private PlayerState last_state = PlayerState.NotReady;
 -        
 +
          // will be changed to PlayerState.Idle after going to PlayerState.Ready
 -        private PlayerState idle_state = PlayerState.NotReady; 
 -        
 +        private PlayerState idle_state = PlayerState.NotReady;
 +
          protected abstract void OpenUri (SafeUri uri);
  
          internal protected virtual bool DelayedInitialize {
@@@ -160,9 -203,16 +203,16 @@@
          {
              OnEventChanged (new PlayerEventArgs (evnt));
          }
 -        
 +
          protected virtual void OnEventChanged (PlayerEventArgs args)
          {
+             if (args.Event == PlayerEvent.StartOfStream && pending_track != null) {
+                 Log.DebugFormat ("OnEventChanged called with StartOfStream.  Replacing current_track: \"{0}\" with pending_track: \"{1}\"", current_track.DisplayTrackTitle, pending_track.DisplayTrackTitle);
+                 current_track = pending_track;
+                 current_uri = pending_uri;
+                 pending_track = null;
+                 pending_uri = null;
+             }
              if (ThreadAssist.InMainThread) {
                  RaiseEventChanged (args);
              } else {
diff --cc src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
index 2ad3f74,fffc1b0..22cd520
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
@@@ -246,10 -246,14 +246,14 @@@ namespace Banshee.MediaEngin
                  }
              }
  
+             if (args.Event == PlayerEvent.StartOfStream) {
+                 incremented_last_played = false;
+             }
 -            
++
              RaiseEvent (args);
 -            
 +
              // Do not raise iterate across DBus to avoid so many calls;
 -            // DBus clients should do their own iterating and 
 +            // DBus clients should do their own iterating and
              // event/state checking locally
              if (args.Event == PlayerEvent.Iterate) {
                  return;
@@@ -303,6 -307,47 +307,46 @@@
              OpenCheck (new SafeUri (uri));
          }
  
 -
+         public void SetNextTrack (TrackInfo track)
+         {
+             if (track != null && EnsureActiveEngineCanPlay (track.Uri)) {
+                 active_engine.SetNextTrack (track);
+             } else {
+                 active_engine.SetNextTrack ((TrackInfo) null);
+             }
+         }
+ 
+         public void SetNextTrack (SafeUri uri)
+         {
+             if (EnsureActiveEngineCanPlay (uri)) {
+                 active_engine.SetNextTrack (uri);
+             } else {
+                 active_engine.SetNextTrack ((SafeUri) null);
+             }
+         }
+ 
+         private bool EnsureActiveEngineCanPlay (SafeUri uri)
+         {
+             if (uri == null) {
+                 // No engine can play the null URI.
+                 return false;
+             }
+             if (active_engine != FindSupportingEngine (uri)) {
+                 if (active_engine.CurrentState == PlayerState.Playing) {
+                     // If we're currently playing then we can't switch engines now.
+                     // We can't ensure the active engine can play this URI.
+                     return false;
+                 } else {
+                     // If we're not playing, we can switch the active engine to
+                     // something that will play this URI.
+                     SwitchToEngine (FindSupportingEngine (uri));
+                     CheckPending ();
+                     return true;
+                 }
+             }
+             return true;
+         }            
+ 
          public void OpenPlay (TrackInfo track)
          {
              OpenPlay (track, true);
@@@ -349,17 -394,14 +393,14 @@@
                  return;
              }
  
-             IncrementLastPlayed ();
- 
-             FindSupportingEngine (uri);
+             PlayerEngine supportingEngine = FindSupportingEngine (uri);
+             SwitchToEngine (supportingEngine);
              CheckPending ();
 -            
 +
              if (track != null) {
                  active_engine.Open (track);
-                 incremented_last_played = false;
              } else if (uri != null) {
                  active_engine.Open (uri);
-                 incremented_last_played = false;
              }
  
              if (play) {
@@@ -380,22 -422,18 +421,18 @@@
                  incremented_last_played = true;
              }
          }
 -        
 +
-         private void FindSupportingEngine (SafeUri uri)
+         private PlayerEngine FindSupportingEngine (SafeUri uri)
          {
              foreach (PlayerEngine engine in engines) {
                  foreach (string extension in engine.ExplicitDecoderCapabilities) {
                      if (!uri.AbsoluteUri.EndsWith (extension)) {
                          continue;
-                     } else if (active_engine != engine) {
-                         Close ();
-                         pending_engine = engine;
-                         Log.DebugFormat ("Switching engine to: {0}", engine.GetType ());
                      }
-                     return;
+                     return engine;
                  }
              }
 -        
 +
              foreach (PlayerEngine engine in engines) {
                  foreach (string scheme in engine.SourceCapabilities) {
                      bool supported = scheme == uri.Scheme;
@@@ -409,8 -442,22 +441,22 @@@
                      }
                  }
              }
+             // If none of our engines support this URI, return the currently active one.
+             // There doesn't seem to be anything better to do.
+             return active_engine;
+         }
+ 
+         private bool SwitchToEngine (PlayerEngine switchTo)
+         {
+             if (active_engine != switchTo) {
+                 Close ();
+                 pending_engine = switchTo;
+                 Log.DebugFormat ("Switching engine to: {0}", switchTo.GetType ());
+                 return true;
+             }
+             return false;
          }
 -        
 +
          public void Close ()
          {
              Close (false);
@@@ -644,10 -691,11 +690,11 @@@
              | PlayerEvent.Error
              | PlayerEvent.Volume
              | PlayerEvent.Metadata
-             | PlayerEvent.TrackInfoUpdated;
+             | PlayerEvent.TrackInfoUpdated
+             | PlayerEvent.RequestNextTrack;
 -        
 +
          private const PlayerEvent event_default_mask = event_all_mask & ~PlayerEvent.Iterate;
 -        
 +
          private static void VerifyEventMask (PlayerEvent eventMask)
          {
              if (eventMask <= PlayerEvent.None || eventMask > event_all_mask) {
diff --cc src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs
index 96b1b79,aa982ea..a5c714f
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs
@@@ -108,10 -108,11 +108,11 @@@ namespace Banshee.MediaEngin
          Error = 64,
          Volume = 128,
          Metadata = 256,
-         TrackInfoUpdated = 512
+         TrackInfoUpdated = 512,
+         RequestNextTrack = 1024
      }
 -    
 -    public enum PlayerState 
 +
 +    public enum PlayerState
      {
          NotReady,
          Ready,
diff --cc src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
index 577c6f8,4e454bc..0376103
--- a/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
+++ b/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
@@@ -90,10 -90,11 +90,11 @@@ namespace Banshee.PlaybackControlle
              player_engine = ServiceManager.PlayerEngine;
              player_engine.PlayWhenIdleRequest += OnPlayerEnginePlayWhenIdleRequest;
              player_engine.ConnectEvent (OnPlayerEvent,
+                 PlayerEvent.RequestNextTrack |
 -                PlayerEvent.EndOfStream | 
 +                PlayerEvent.EndOfStream |
                  PlayerEvent.StartOfStream |
                  PlayerEvent.StateChange |
 -                PlayerEvent.Error, 
 +                PlayerEvent.Error,
                  true);
  
              ServiceManager.SourceManager.ActiveSourceChanged += delegate {
@@@ -164,9 -165,12 +165,12 @@@
                          transition_track_started = true;
                      }
                      break;
+                 case PlayerEvent.RequestNextTrack:
+                     RequestTrackHandler ();
+                     break;
 -            }       
 +            }
          }
 -        
 +
          private void CancelErrorTransition ()
          {
              if (error_transition_id > 0) {
@@@ -205,29 -215,36 +215,36 @@@
              } else {
                  ((IBasicPlaybackController)this).First ();
              }
 -            
 +
              //OnTransition ();
          }
 -        
 +
          public void Next ()
          {
-             Next (RepeatMode == PlaybackRepeatMode.RepeatAll);
+             Next (RepeatMode == PlaybackRepeatMode.RepeatAll, true);
          }
  
          public void Next (bool restart)
          {
+             Next (restart, true);
+         }
+         
+         public void Next (bool restart, bool userRequested)
+         {
              CancelErrorTransition ();
 -            
 +
              Source = NextSource;
              raise_started_after_transition = true;
  
-             player_engine.IncrementLastPlayed ();
+             if (userRequested) {
+                 player_engine.IncrementLastPlayed ();
+             }
 -            
 +
-             if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Next (restart)) {
+             if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Next (restart, userRequested)) {
              } else {
-                 ((IBasicPlaybackController)this).Next (restart);
+                 ((IBasicPlaybackController)this).Next (restart, userRequested);
              }
 -            
 +
              OnTransition ();
          }
  
@@@ -260,54 -277,51 +277,51 @@@
              }
              return true;
          }
 -        
 +
-         bool IBasicPlaybackController.Next (bool restart)
+         bool IBasicPlaybackController.Next (bool restart, bool userRequested)
          {
-             TrackInfo tmp_track = CurrentTrack;
- 
-             if (next_stack.Count > 0) {
-                 CurrentTrack = next_stack.Pop ();
-                 if (tmp_track != null) {
-                     previous_stack.Push (tmp_track);
-                 }
-             } else {
-                 TrackInfo next_track = QueryTrack (Direction.Next, restart);
-                 if (next_track != null) {
-                     if (tmp_track != null) {
-                         previous_stack.Push (tmp_track);
-                     }
-                 } else {
-                     return true;
-                 }
- 
-                 CurrentTrack = next_track;
+             if (CurrentTrack != null) {
+                 previous_stack.Push (CurrentTrack);
              }
  
-             QueuePlayTrack ();
+             CurrentTrack = CalcNextTrack (Direction.Next, restart);
+             if (!userRequested) {
+                 // A RequestNextTrack event should always result in SetNextTrack being called.  null is acceptable.
+                 player_engine.SetNextTrack (CurrentTrack);
+             } else if (CurrentTrack != null) {
+                 QueuePlayTrack ();
+             }
              return true;
          }
 -        
 +
          bool IBasicPlaybackController.Previous (bool restart)
          {
              if (CurrentTrack != null && previous_stack.Count > 0) {
                  next_stack.Push (current_track);
              }
  
-             if (previous_stack.Count > 0) {
-                 CurrentTrack = previous_stack.Pop ();
-             } else {
-                 TrackInfo track = CurrentTrack = QueryTrack (Direction.Previous, restart);
-                 if (track != null) {
-                     CurrentTrack = track;
-                 } else {
-                     return true;
-                 }
+             CurrentTrack = CalcNextTrack (Direction.Previous, restart);
+             if (CurrentTrack != null) {
+                 QueuePlayTrack ();
              }
 -            
 +
              return true;
          }
  
+         private TrackInfo CalcNextTrack (Direction direction, bool restart)
+         {
+             if (direction == Direction.Previous) {
+                 if (previous_stack.Count > 0) {
+                     return previous_stack.Pop ();
+                 }
+             } else if (direction == Direction.Next) {
+                 if (next_stack.Count > 0) {
+                     return next_stack.Pop ();
+                 }
+             }
+             return QueryTrack (direction, restart);
+         }
 -        
++
          private TrackInfo QueryTrack (Direction direction, bool restart)
          {
              Log.DebugFormat ("Querying model for track to play in {0}:{1} mode", ShuffleMode, direction);
diff --cc src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
index 7447515,e066c5d..5d700b7
--- a/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
+++ b/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
@@@ -278,9 -278,21 +278,21 @@@ namespace Banshee.InternetRadi
          {
              return false;
          }
 -        
 +
-         public bool Next (bool restart)
+         public bool Next (bool restart, bool userRequested)
          {
+             /*
+              * TODO: It should be technically possible to handle userRequested=False
+              * correctly here, but the current implementation is quite hostile.
+              * For the moment, just SetNextTrack (null), and go on to OpenPlay if
+              * the engine isn't currently playing.
+              */
+             if (!userRequested) {
+                 ServiceManager.PlayerEngine.SetNextTrack ((SafeUri)null);
+                 if (ServiceManager.PlayerEngine.IsPlaying ()) {
+                     return true;
+                 }
+             }
              RadioTrackInfo radio_track = ServiceManager.PlaybackController.CurrentTrack as RadioTrackInfo;
              if (radio_track != null && radio_track.PlayNextStream ()) {
                  return true;
diff --cc src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
index 2aeffce,bf3651e..6238bd0
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
+++ b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
@@@ -294,17 -294,24 +294,24 @@@ namespace Banshee.Lastfm.Radi
  
          bool IBasicPlaybackController.First ()
          {
-             return ((IBasicPlaybackController)this).Next (false);
+             return ((IBasicPlaybackController)this).Next (false, true);
          }
 -        
 +
-         private bool playback_requested;
-         bool IBasicPlaybackController.Next (bool restart)
+         private bool playback_requested;    
+         bool IBasicPlaybackController.Next (bool restart, bool userRequested)
          {
              TrackInfo next = NextTrack;
-             if (next != null) {
-                 ServiceManager.PlayerEngine.OpenPlay (next);
-             }  else {
-                 playback_requested = true;
+             if (userRequested) {
+                 if (next != null) {
+                     ServiceManager.PlayerEngine.OpenPlay (next);
+                 }  else {
+                     playback_requested = true;
+                 }
+             } else {
+                 // We want to unconditionally SetNextTrack.
+                 // Passing null is OK.
+                 ServiceManager.PlayerEngine.SetNextTrack (next);
+                 playback_requested = next == null;
              }
              return true;
          }
diff --cc src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
index 25b1687,6a1d054..6eec3aa
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
@@@ -518,10 -478,10 +518,10 @@@ namespace Banshee.PlayQueu
  
          bool IBasicPlaybackController.First ()
          {
-             return ((IBasicPlaybackController)this).Next (false);
+             return ((IBasicPlaybackController)this).Next (false, true);
          }
 -        
 +
-         bool IBasicPlaybackController.Next (bool restart)
+         bool IBasicPlaybackController.Next (bool restart, bool userRequested)
          {
              if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {
                  int index = TrackModel.IndexOf (current_track) + 1;
@@@ -539,10 -502,14 +542,14 @@@
                  return true;
              }
  
-             ServiceManager.PlayerEngine.OpenPlay (current_track);
+             if (userRequested) {
+                 ServiceManager.PlayerEngine.OpenPlay (current_track);
+             } else {
+                 ServiceManager.PlayerEngine.SetNextTrack (current_track);
+             }
              return true;
          }
 -        
 +
          bool IBasicPlaybackController.Previous (bool restart)
          {
              if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]