diff -Nur test-git/docs/Banshee/en/Banshee.MediaEngine/PlayerEngineService.xml gapless-cleanup/docs/Banshee/en/Banshee.MediaEngine/PlayerEngineService.xml
--- test-git/docs/Banshee/en/Banshee.MediaEngine/PlayerEngineService.xml	2009-05-03 14:26:22.000000000 +1000
+++ gapless-cleanup/docs/Banshee/en/Banshee.MediaEngine/PlayerEngineService.xml	2009-04-09 16:43:02.000000000 +1000
@@ -245,5 +245,35 @@
       <ReturnValue><ReturnType>Banshee.Configuration.SchemaEntry&lt;System.Int32&gt;</ReturnType></ReturnValue>
       <Docs><summary>To be added.</summary><remarks>To be added.</remarks></Docs>
     <AssemblyInfo><AssemblyVersion>1.3.1.0</AssemblyVersion><AssemblyVersion>1.4.0.0</AssemblyVersion></AssemblyInfo></Member>
+
+    <Member MemberName="SetNextTrack">
+      <MemberSignature Language="C#" Value="public bool SetNextTrack (TrackInfo track);"/>
+      <MemberType>Method</MemberType>
+      <ReturnValue><ReturnType>System.Boolean</ReturnType></ReturnValue>
+      <AssemblyInfo><AssemblyVersion>1.5.0.0</AssemblyVersion></AssemblyInfo>
+      <Docs>
+        <summary>Give the PlayerEngine the next track, so that it can buffer it for gapless/crossfading playback.</summary>
+        <param name="track">The <see cref="TrackInfo"/> to buffer</param>
+        <returns>
+          True iff the engine successfully set <paramref name="track"/> to be pending.  In this case, playback will automatically
+          continue when the currently playing track ends.
+        </returns>
+      </Docs>
+    </Member>
+
+    <Member MemberName="SetNextTrack">
+      <MemberSignature Language="C#" Value="public bool SetNextTrack (SafeUri uri);"/>
+      <MemberType>Method</MemberType>
+      <ReturnValue><ReturnType>System.Boolean</ReturnType></ReturnValue>
+      <AssemblyInfo><AssemblyVersion>1.5.0.0</AssemblyVersion></AssemblyInfo>
+      <Docs>
+        <summary>Give the PlayerEngine the next track, so that it can buffer it for gapless/crossfading playback.</summary>
+        <param name="uri">The <see cref="SafeUri"/> to buffer</param>
+        <returns>
+          True iff the engine successfully set <paramref name="uri"/> to be pending.  In this case, playback will automatically
+          continue when the currently playing track ends.
+        </returns>
+      </Docs>
+    </Member>
   </Members>
 </Type>
diff -Nur test-git/libbanshee/banshee-player.c gapless-cleanup/libbanshee/banshee-player.c
--- test-git/libbanshee/banshee-player.c	2009-05-03 14:26:16.000000000 +1000
+++ gapless-cleanup/libbanshee/banshee-player.c	2009-05-01 15:56:36.000000000 +1000
@@ -198,6 +198,14 @@
 }
 
 P_INVOKE gboolean
+bp_set_next_track (BansheePlayer *player, const gchar *uri)
+{
+	g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
+	player->next_uri = g_strdup(uri);
+	return TRUE;
+}
+
+P_INVOKE gboolean
 bp_set_position (BansheePlayer *player, guint64 time_ms)
 {
     g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
@@ -347,3 +355,9 @@
     *resource = GST_RESOURCE_ERROR;
     *stream = GST_STREAM_ERROR;
 }
+
+P_INVOKE void
+bp_set_next_track_starting_callback (BansheePlayer *player, BansheePlayerNextTrackStartingCallback *cb)
+{
+    SET_CALLBACK (next_track_starting_cb);
+}
diff -Nur test-git/libbanshee/banshee-player-pipeline.c gapless-cleanup/libbanshee/banshee-player-pipeline.c
--- test-git/libbanshee/banshee-player-pipeline.c	2009-05-03 14:26:17.000000000 +1000
+++ gapless-cleanup/libbanshee/banshee-player-pipeline.c	2009-05-01 15:56:36.000000000 +1000
@@ -172,6 +172,23 @@
     return TRUE;
 }
 
+static void bp_about_to_finish_callback (GstElement *playbin, BansheePlayer *player)
+{
+    if (player->next_uri != NULL) {
+        bp_debug ("Gapless: Setting uri to: %s", player->next_uri);
+        g_object_set (G_OBJECT (playbin), "uri", player->next_uri, NULL);
+        g_free (player->next_uri);
+        player->next_uri = NULL;
+    } else {
+        bp_debug ("Gapless: about-to-finish called, but player->next_track not set");
+    }
+    //playbin2 currently doesn't (yet) have any way to notify us when it actually starts playing the next track.
+    //On linux, about-to-finish should be called ~200ms before the next track starts.  This'll be good enough for now.
+    if (player->next_track_starting_cb != NULL) {
+        player->next_track_starting_cb (player);
+    }
+}
+
 // ---------------------------------------------------------------------------
 // Internal Functions
 // ---------------------------------------------------------------------------
@@ -190,8 +207,14 @@
     
     // Playbin is the core element that handles autoplugging (finding the right
     // source and decoder elements) based on source URI and stream content
-    player->playbin = gst_element_factory_make ("playbin", "playbin");
+    player->playbin = gst_element_factory_make ("playbin2", "playbin");
     g_return_val_if_fail (player->playbin != NULL, FALSE);
+    
+    // It seems that playbin2 is terribly sensitive about precisely when setting "uri" to queue the next track
+    // will work.  So, handle the callback in native code, using player->next_uri which can be set at leisure from
+    // native code.
+    g_signal_connect (player->playbin, "about-to-finish", G_CALLBACK (bp_about_to_finish_callback), player);
+
 
     // Try to find an audio sink, prefer gconf, which typically is set to auto these days,
     // fall back on auto, which should work on windows, and as a last ditch, try alsa
diff -Nur test-git/libbanshee/banshee-player-private.h gapless-cleanup/libbanshee/banshee-player-private.h
--- test-git/libbanshee/banshee-player-private.h	2009-05-03 14:26:17.000000000 +1000
+++ gapless-cleanup/libbanshee/banshee-player-private.h	2009-05-01 15:56:36.000000000 +1000
@@ -70,6 +70,7 @@
 typedef void (* BansheePlayerBufferingCallback)    (BansheePlayer *player, gint buffering_progress);
 typedef void (* BansheePlayerTagFoundCallback)     (BansheePlayer *player, const gchar *tag, const GValue *value);
 typedef void (* BansheePlayerVisDataCallback)      (BansheePlayer *player, gint channels, gint samples, gfloat *data, gfloat *spectrum);
+typedef void (* BansheePlayerNextTrackStartingCallback)    (BansheePlayer *player);
 
 struct BansheePlayer {
     // Player Callbacks
@@ -80,6 +81,7 @@
     BansheePlayerBufferingCallback buffering_cb;
     BansheePlayerTagFoundCallback tag_found_cb;
     BansheePlayerVisDataCallback vis_data_cb;
+    BansheePlayerNextTrackStartingCallback next_track_starting_cb;
 
     // Pipeline Elements
     GstElement *playbin;
@@ -139,6 +141,8 @@
     gdouble album_peak;
     gdouble track_gain;
     gdouble track_peak;
+    
+    gchar *next_uri;
 };
 
 #endif /* _BANSHEE_PLAYER_PRIVATE_H */
diff -Nur test-git/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs gapless-cleanup/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
--- test-git/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs	2009-05-03 14:26:21.000000000 +1000
+++ gapless-cleanup/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs	2009-05-01 16:01:56.000000000 +1000
@@ -57,6 +57,7 @@
     internal delegate void BansheePlayerIterateCallback (IntPtr player);
     internal delegate void BansheePlayerBufferingCallback (IntPtr player, int buffering_progress);
     internal delegate void BansheePlayerVisDataCallback (IntPtr player, int channels, int samples, IntPtr data, IntPtr spectrum);
+    internal delegate void BansheePlayerNextTrackStartingCallback (IntPtr player);
 
     internal delegate void GstTaggerTagFoundCallback (IntPtr player, string tagName, ref GLib.Value value);
     
@@ -76,10 +77,15 @@
         private BansheePlayerBufferingCallback buffering_callback;
         private BansheePlayerVisDataCallback vis_data_callback;
         private GstTaggerTagFoundCallback tag_found_callback;
+        private BansheePlayerNextTrackStartingCallback next_track_starting_callback;
         
         private bool buffering_finished;
         private int pending_volume = -1;
         private bool xid_is_set = false;
+		private uint last_position;
+		private bool next_track_set = false;
+
+        private bool GaplessEnabled;
         
         private event VisualizationDataHandler data_available = null;
         public event VisualizationDataHandler DataAvailable {
@@ -143,13 +149,15 @@
             buffering_callback = new BansheePlayerBufferingCallback (OnBuffering);
             vis_data_callback = new BansheePlayerVisDataCallback (OnVisualizationData);
             tag_found_callback = new GstTaggerTagFoundCallback (OnTagFound);
-            
+            next_track_starting_callback = new BansheePlayerNextTrackStartingCallback (OnNextTrackStarting);
+
             bp_set_eos_callback (handle, eos_callback);
             bp_set_iterate_callback (handle, iterate_callback);
             bp_set_error_callback (handle, error_callback);
             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);
             
             OnStateChanged (PlayerState.Ready);
             
@@ -159,6 +167,7 @@
             
             InstallPreferences ();
             ReplayGainEnabled = ReplayGainEnabledSchema.Get ();
+            GaplessEnabled = GaplessEnabledSchema.Get ();
         }
         
         public override void Dispose ()
@@ -206,6 +215,17 @@
         {
             bp_pause (handle);
         }
+
+		public override bool SetNextTrackUri (SafeUri uri)
+		{
+			IntPtr uri_ptr = GLib.Marshaller.StringToPtrGStrdup (uri.AbsoluteUri);
+			try {
+			     bp_set_next_track (handle, uri_ptr);
+			} finally {
+				GLib.Marshaller.Free (uri_ptr);
+			}
+            return true;
+		}
         
         public override void VideoExpose (IntPtr window, bool direct)
         {
@@ -228,9 +248,22 @@
             Close (false);
             OnEventChanged (PlayerEvent.EndOfStream);
         }
+
+        private void OnNextTrackStarting (IntPtr player)
+        {
+            if (GaplessEnabled) {
+                OnEventChanged (PlayerEvent.StartOfStream);
+            }
+            next_track_set = false;
+        }
         
         private void OnIterate (IntPtr player)
         {
+			if (GaplessEnabled && (((Length - Position) < 1000 * 5) && !next_track_set)) {
+				OnEventChanged (PlayerEvent.BufferNextTrack);
+				next_track_set = true;
+			}
+
             OnEventChanged (PlayerEvent.Iterate);
         }
         
@@ -489,6 +522,7 @@
 #region Preferences
 
         private PreferenceBase replaygain_preference;
+        private PreferenceBase gapless_preference;
 
         private void InstallPreferences ()
         {
@@ -502,6 +536,11 @@
                 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 (EXPERIMENTAL)"),
+                Catalog.GetString ("Eliminate the small playback gap on track change.  Useful for concept albums & classical music."),
+                delegate { GaplessEnabled = GaplessEnabledSchema.Get (); }
+            ));                            
         }
         
         private void UninstallPreferences ()
@@ -512,7 +551,9 @@
             }
             
             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> (
@@ -522,6 +563,14 @@
             "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",
+            false,
+            "Enable gapless playback (EXPERIMENTAL)",
+            "Eliminate the small playback gap on track change.  Useful for concept albums & classical music.  EXPERIMENTAL"
+        );
+                                                                                              
+
 #endregion
         
         [DllImport ("libbanshee")]
@@ -554,6 +603,10 @@
         [DllImport ("libbanshee")]
         private static extern void bp_set_tag_found_callback (HandleRef player,
             GstTaggerTagFoundCallback cb);
+
+        [DllImport ("libbanshee")]
+        private static extern void bp_set_next_track_starting_callback (HandleRef player,
+            BansheePlayerNextTrackStartingCallback cb);
         
         [DllImport ("libbanshee")]
         private static extern bool bp_open (HandleRef player, IntPtr uri);
@@ -568,6 +621,9 @@
         private static extern void bp_play (HandleRef player);
         
         [DllImport ("libbanshee")]
+        private static extern bool bp_set_next_track (HandleRef player, IntPtr uri);
+
+		[DllImport ("libbanshee")]
         private static extern void bp_set_volume (HandleRef player, double volume);
         
         [DllImport("libbanshee")]
Binary files test-git/src/Backends/Banshee.NowPlaying.X11/libbnpx11/libbnpx11_la-bacon-resize.o and gapless-cleanup/src/Backends/Banshee.NowPlaying.X11/libbnpx11/libbnpx11_la-bacon-resize.o differ
diff -Nur test-git/src/Clients/Halie/Halie/Client.cs gapless-cleanup/src/Clients/Halie/Halie/Client.cs
--- test-git/src/Clients/Halie/Halie/Client.cs	2009-05-03 14:26:17.000000000 +1000
+++ gapless-cleanup/src/Clients/Halie/Halie/Client.cs	2009-03-30 07:44:58.000000000 +1100
@@ -131,7 +131,7 @@
                     
                     // For the playback controller
                     case "first":    controller.First ();                                    break;
-                    case "next":     controller.Next (ParseBool (arg.Value, "restart"));     break;
+                    case "next":     controller.Next (ParseBool (arg.Value, "restart"), true);     break;
                     case "previous": controller.Previous (ParseBool (arg.Value, "restart")); break;
                     case "stop-when-finished": 
                         controller.StopWhenFinished = !ParseBool (arg.Value);
diff -Nur test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
--- test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs	2009-05-03 14:26:22.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs	2009-04-26 17:01:32.000000000 +1000
@@ -47,6 +47,8 @@
         
         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;
         
@@ -86,7 +88,42 @@
             current_track = new UnknownTrackInfo (uri);
             
             HandleOpen (uri);
-        }
+		}
+
+		public bool SetNextTrack (TrackInfo track)
+		{
+			pending_uri = track.Uri;
+			pending_track = track;
+
+			return HandleNextTrack (pending_uri);
+		}
+		
+		public bool SetNextTrack (SafeUri uri)
+		{
+			pending_uri = uri;
+			pending_track = new UnknownTrackInfo (uri);
+
+			return HandleNextTrack (uri);
+		}
+
+		private bool HandleNextTrack (SafeUri uri)
+		{
+			if (current_state != PlayerState.Playing) {
+				// Pre-buffering the next track only makes sense when we're currently playing
+                // Instead, just open.
+				HandleOpen (uri);
+                return true;
+			}
+
+			try {
+				// Setting the next track doesn't change the player state.
+				SetNextTrackUri (uri);
+			} catch (Exception e) {
+				Log.Exception ("Failed to pre-buffer next track", e);
+				return false;
+			}
+			return true;
+		}
 
         private void HandleOpen (SafeUri uri)
         {
@@ -107,6 +144,11 @@
 
         public abstract void Pause ();
 
+		public virtual bool SetNextTrackUri (SafeUri uri)
+		{
+            return false;
+        }
+
         public virtual void VideoExpose (IntPtr displayContext, bool direct)
         {
             throw new NotImplementedException ("Engine must implement VideoExpose since this method only gets called when SupportsVideo is true");
@@ -153,6 +195,13 @@
         
         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 -Nur test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
--- test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	2009-05-03 14:26:18.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	2009-04-17 14:41:27.000000000 +1000
@@ -268,7 +268,26 @@
         {
             OpenCheck (new SafeUri (uri));
         }
-        
+
+
+		public bool SetNextTrack (TrackInfo track)
+		{
+			if (active_engine != FindSupportingEngine (track.Uri)) {
+				// If the current engine does not support the next track, it will be extremely difficult to make the
+				// transition across playback engines gapless.  Just refuse to try, instead.
+				return false;
+			}
+			return active_engine.SetNextTrack (track);
+		}
+
+		public bool SetNextTrack (SafeUri uri)
+		{
+			if (active_engine != FindSupportingEngine (uri)) {
+				return false;
+			}
+			return active_engine.SetNextTrack (uri);
+		}
+
         public void OpenPlay (TrackInfo track)
         {
             OpenPlay (track, true);
@@ -317,7 +336,8 @@
 
             IncrementLastPlayed ();
             
-            FindSupportingEngine (uri);
+            PlayerEngine supportingEngine = FindSupportingEngine (uri);
+			SwitchToEngine (supportingEngine);
             CheckPending ();
             
             if (track != null) {
@@ -347,35 +367,40 @@
             }
         }
         
-        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;
-                    if (supported && active_engine != engine) {
-                        Close ();
-                        pending_engine = engine;
-                        Log.DebugFormat ("Switching engine to: {0}", engine.GetType ());
-                        return;
-                    } else if (supported) {
-                        return;
+                    if (supported) {
+						return engine;
                     }
                 }
             }
+			// 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 ()
         {
@@ -610,7 +635,8 @@
             | PlayerEvent.Error
             | PlayerEvent.Volume
             | PlayerEvent.Metadata
-            | PlayerEvent.TrackInfoUpdated;
+            | PlayerEvent.TrackInfoUpdated
+            | PlayerEvent.BufferNextTrack;
         
         private const PlayerEvent event_default_mask = event_all_mask & ~PlayerEvent.Iterate;
         
diff -Nur test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs
--- test-git/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs	2009-05-03 14:26:17.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEvent.cs	2009-03-19 17:26:29.000000000 +1100
@@ -108,7 +108,8 @@
         Error = 64,
         Volume = 128,
         Metadata = 256,
-        TrackInfoUpdated = 512
+        TrackInfoUpdated = 512,
+        BufferNextTrack = 1024
     }
     
     public enum PlayerState 
diff -Nur test-git/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs
--- test-git/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	2009-05-03 14:26:18.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	2009-03-30 07:45:31.000000000 +1100
@@ -31,7 +31,7 @@
     public interface IBasicPlaybackController
     {
         bool First ();
-        bool Next (bool restart);
+        bool Next (bool restart, bool immediate);
         bool Previous (bool restart);
     }
 }
diff -Nur test-git/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs
--- test-git/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs	2009-05-03 14:26:22.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs	2009-03-27 21:45:42.000000000 +1100
@@ -46,7 +46,7 @@
         event PlaybackControllerStoppedHandler Stopped;
         
         void First ();
-        void Next (bool restart);
+        void Next (bool restart, bool immediate);
         void Previous (bool restart);
         
         PlaybackShuffleMode ShuffleMode { get; set; }
diff -Nur test-git/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
--- test-git/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	2009-05-03 14:26:18.000000000 +1000
+++ gapless-cleanup/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	2009-04-09 16:59:19.000000000 +1000
@@ -92,8 +92,9 @@
                 PlayerEvent.EndOfStream | 
                 PlayerEvent.StartOfStream |
                 PlayerEvent.StateChange |
-                PlayerEvent.Error, 
-                true);
+                PlayerEvent.Error |
+			    PlayerEvent.BufferNextTrack,
+			    true);
 
             ServiceManager.SourceManager.ActiveSourceChanged += delegate {
                 ITrackModelSource active_source = ServiceManager.SourceManager.ActiveSource as ITrackModelSource;
@@ -128,9 +129,15 @@
             switch (args.Event) {
                 case PlayerEvent.StartOfStream:
                     consecutive_errors = 0;
+				Log.DebugFormat ("ControllerService: StartOfStream signalled.  Our current track: {0}, playback_engine.CurrentTrack: {1}", CurrentTrack == null ? "None" : CurrentTrack.Uri.AbsoluteUri,
+				                 player_engine.CurrentTrack == null ? "None" : player_engine.CurrentTrack.Uri.AbsoluteUri);
+				CurrentTrack = player_engine.CurrentTrack;
                     break;
                 case PlayerEvent.EndOfStream:
-                    EosTransition ();
+                    NextTrackTransition (true);
+                    break;
+                case PlayerEvent.BufferNextTrack:
+                    NextTrackTransition (false);
                     break;
                 case PlayerEvent.Error:
                     if (++consecutive_errors >= 5) {
@@ -142,7 +149,7 @@
                     
                     CancelErrorTransition ();
                     // TODO why is this so long? any reason not to be instantaneous?
-                    Application.RunTimeout (250, EosTransition);
+                    Application.RunTimeout (250, delegate {return NextTrackTransition (true);});
                     break;
                 case PlayerEvent.StateChange:
                     if (((PlayerEventStateChangeArgs)args).Current != PlayerState.Loading) {
@@ -174,13 +181,13 @@
             }
         }
         
-        private bool EosTransition ()
+        private bool NextTrackTransition (bool immediate)
         {
             if (!StopWhenFinished) {
                 if (RepeatMode == PlaybackRepeatMode.RepeatSingle) {
                     QueuePlayTrack ();
                 } else {
-                    Next ();
+                    Next (RepeatMode == PlaybackRepeatMode.RepeatAll, immediate);
                 }
             } else {
                 OnStopped ();
@@ -209,10 +216,10 @@
         
         public void Next ()
         {
-            Next (RepeatMode == PlaybackRepeatMode.RepeatAll);
+            Next (RepeatMode == PlaybackRepeatMode.RepeatAll, true);
         }
         
-        public void Next (bool restart)
+        public void Next (bool restart, bool immediate)
         {
             CancelErrorTransition ();
             
@@ -221,9 +228,9 @@
 
             player_engine.IncrementLastPlayed ();
             
-            if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Next (restart)) {
+            if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Next (restart, immediate)) {
             } else {
-                ((IBasicPlaybackController)this).Next (restart);
+                ((IBasicPlaybackController)this).Next (restart, immediate);
             }
             
             OnTransition ();
@@ -259,12 +266,13 @@
             return true;
         }
         
-        bool IBasicPlaybackController.Next (bool restart)
+        bool IBasicPlaybackController.Next (bool restart, bool immediate)
         {
+            Log.DebugFormat ("PlaybackControllerService.Next ({0}, {1}) called", restart, immediate);
             TrackInfo tmp_track = CurrentTrack;
 
             if (next_stack.Count > 0) {
-                CurrentTrack = next_stack.Pop ();
+                changing_to_track = next_stack.Pop ();
                 if (tmp_track != null) {
                     previous_stack.Push (tmp_track);
                 }
@@ -277,11 +285,16 @@
                 } else {
                     return true;
                 }
-                
-                CurrentTrack = next_track;
+
+                changing_to_track = next_track;
             }
             
-            QueuePlayTrack ();
+            if (immediate) {
+                CurrentTrack = changing_to_track;
+                QueuePlayTrack ();
+            } else {
+                player_engine.SetNextTrack (changing_to_track);
+            }
             return true;
         }
         
diff -Nur test-git/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs gapless-cleanup/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
--- test-git/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	2009-05-03 14:26:18.000000000 +1000
+++ gapless-cleanup/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	2009-03-30 07:42:29.000000000 +1100
@@ -279,7 +279,7 @@
             return false;
         }
         
-        public bool Next (bool restart)
+        public bool Next (bool restart, bool immediate)
         {
             RadioTrackInfo radio_track = ServiceManager.PlaybackController.CurrentTrack as RadioTrackInfo;
             if (radio_track != null && radio_track.PlayNextStream ()) {
diff -Nur test-git/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs gapless-cleanup/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
--- test-git/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	2009-05-03 14:26:17.000000000 +1000
+++ gapless-cleanup/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	2009-03-30 07:43:27.000000000 +1100
@@ -294,15 +294,19 @@
 
         bool IBasicPlaybackController.First ()
         {
-            return ((IBasicPlaybackController)this).Next (false);
+            return ((IBasicPlaybackController)this).Next (false, true);
         }
         
         private bool playback_requested;    
-        bool IBasicPlaybackController.Next (bool restart)
+        bool IBasicPlaybackController.Next (bool restart, bool immediate)
         {
             TrackInfo next = NextTrack;
             if (next != null) {
-                ServiceManager.PlayerEngine.OpenPlay (next);
+                if (immediate) {
+                    ServiceManager.PlayerEngine.OpenPlay (next);
+                } else {
+                    ServiceManager.PlayerEngine.SetNextTrack (next);
+                }
             }  else {
                 playback_requested = true;
             }
@@ -372,7 +376,7 @@
 
                             if (playback_requested) {
                                 if (this == ServiceManager.PlaybackController.Source ) {
-                                    ((IBasicPlaybackController)this).Next (false);
+                                    ((IBasicPlaybackController)this).Next (false, true);
                                 }
                                 playback_requested = false;
                             }
diff -Nur test-git/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs gapless-cleanup/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
--- test-git/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	2009-05-03 14:26:22.000000000 +1000
+++ gapless-cleanup/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	2009-05-03 15:02:10.000000000 +1000
@@ -235,25 +235,32 @@
 
         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 immediate)
         {
+            
+           Hyena.Log.DebugFormat ("PlayQueue.Next({0},{1}) called", restart, immediate);
+//            Hyena.Log.DebugFormat ("{0} - {1} - {2}", TrackModel[0].LocalPath, TrackModel[1].LocalPath, TrackModel[2].LocalPath);
             RemovePlayingTrack ();
             
             if (Count == 0) {
                 ServiceManager.PlaybackController.Source = PriorSource;
                 if (was_playing) {
                     ServiceManager.PlaybackController.PriorTrack = prior_playback_track;
-                    ServiceManager.PlaybackController.Next (restart);
+                    ServiceManager.PlaybackController.Next (restart, immediate);
                 } else {
                     ServiceManager.PlayerEngine.Close ();
                 }
                 return true;
             }
-            
-            ServiceManager.PlayerEngine.OpenPlay ((DatabaseTrackInfo)TrackModel[0]);
+
+            if (immediate) {
+                ServiceManager.PlayerEngine.OpenPlay ((DatabaseTrackInfo)TrackModel[0]);
+            } else {
+                ServiceManager.PlayerEngine.SetNextTrack ((DatabaseTrackInfo)TrackModel[0]);
+            }
             return true;
         }
         
@@ -264,7 +271,9 @@
         
         private bool RemovePlayingTrack ()
         {
+            Hyena.Log.Debug ("RemovePlayingTrack called..");
             if (playing_track != null) {
+                Hyena.Log.DebugFormat ("...playing_track is {0}", playing_track.LocalPath);
                 RemoveTrack (playing_track);
                 playing_track = null;
                 return true;
