Index: src/Core/Banshee.Services/Banshee.Services.mdp
===================================================================
--- src/Core/Banshee.Services/Banshee.Services.mdp	(revision 3796)
+++ src/Core/Banshee.Services/Banshee.Services.mdp	(working copy)
@@ -148,6 +148,8 @@
     <File name="Banshee.Collection.Database/CachedList.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.ServiceStack/BatchUserJob.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Sources/IDiskUsageReporter.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.MediaEngine/IVisualizationDataSource.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.MediaEngine/VisualizationDataCallback.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
@@ -172,4 +174,4 @@
     <AsmRefVar />
     <ProjectRefVar />
   </MonoDevelop.Autotools.MakefileInfo>
-</Project>
\ No newline at end of file
+</Project>
Index: src/Core/Banshee.Services/Banshee.MediaEngine/IVisualizationDataSource.cs
===================================================================
--- src/Core/Banshee.Services/Banshee.MediaEngine/IVisualizationDataSource.cs	(revision 0)
+++ src/Core/Banshee.Services/Banshee.MediaEngine/IVisualizationDataSource.cs	(revision 0)
@@ -0,0 +1,36 @@
+// IVisualizationDataSource.cs
+//
+// Author:
+//   Chris Howie <cdhowie@gmail.com>
+//
+// Copyright (C) 2008 Chris Howie
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Banshee.MediaEngine
+{
+    public interface IVisualizationDataSource
+    {
+        event VisualizationDataCallback DataAvailable;
+    }
+}
Index: src/Core/Banshee.Services/Banshee.MediaEngine/VisualizationDataCallback.cs
===================================================================
--- src/Core/Banshee.Services/Banshee.MediaEngine/VisualizationDataCallback.cs	(revision 0)
+++ src/Core/Banshee.Services/Banshee.MediaEngine/VisualizationDataCallback.cs	(revision 0)
@@ -0,0 +1,31 @@
+// VisualizationDataCallback.cs
+//
+// Author:
+//   Chris Howie <cdhowie@gmail.com>
+//
+// Copyright (C) 2008 Chris Howie
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace Banshee.MediaEngine
+{
+    public delegate void VisualizationDataCallback(float[][] data);
+}
\ No newline at end of file
Index: src/Core/Banshee.Services/Makefile.am
===================================================================
--- src/Core/Banshee.Services/Makefile.am	(revision 3796)
+++ src/Core/Banshee.Services/Makefile.am	(working copy)
@@ -53,11 +53,13 @@
 	Banshee.MediaEngine/IAudioCdRipper.cs \
 	Banshee.MediaEngine/IEqualizer.cs \
 	Banshee.MediaEngine/IPlayerEngineService.cs \
+	Banshee.MediaEngine/IVisualizationDataSource.cs \
 	Banshee.MediaEngine/NullPlayerEngine.cs \
 	Banshee.MediaEngine/PlayerEngine.cs \
 	Banshee.MediaEngine/PlayerEngineEvent.cs \
 	Banshee.MediaEngine/PlayerEngineService.cs \
 	Banshee.MediaEngine/PlayerEngineState.cs \
+	Banshee.MediaEngine/VisualizationDataCallback.cs \
 	Banshee.MediaProfiles/MediaProfileManager.cs \
 	Banshee.MediaProfiles/Pipeline.cs \
 	Banshee.MediaProfiles/PipelineVariable.cs \
Index: src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
===================================================================
--- src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs	(revision 3796)
+++ src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs	(working copy)
@@ -45,10 +45,11 @@
     internal delegate void BansheePlayerStateChangedCallback (IntPtr player, int old_state, int new_state, int pending_state);
     internal delegate void BansheePlayerIterateCallback (IntPtr player);
     internal delegate void BansheePlayerBufferingCallback (IntPtr player, int buffering_progress);
+    internal delegate void BansheePlayerVizDataCallback (IntPtr player, int channels, int samples, IntPtr data);
 
     internal delegate void GstTaggerTagFoundCallback (IntPtr player, string tagName, ref GLib.Value value);
     
-    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine, Banshee.MediaEngine.IEqualizer
+    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine, Banshee.MediaEngine.IEqualizer, Banshee.MediaEngine.IVisualizationDataSource
     {
         private uint GST_CORE_ERROR = 0;
         private uint GST_LIBRARY_ERROR = 0;
@@ -62,6 +63,7 @@
         private BansheePlayerStateChangedCallback state_changed_callback;
         private BansheePlayerIterateCallback iterate_callback;
         private BansheePlayerBufferingCallback buffering_callback;
+        private BansheePlayerVizDataCallback viz_data_callback;
         private GstTaggerTagFoundCallback tag_found_callback;
         
         private bool buffering_finished;
@@ -101,6 +103,7 @@
             error_callback = new BansheePlayerErrorCallback (OnError);
             iterate_callback = new BansheePlayerIterateCallback (OnIterate);
             buffering_callback = new BansheePlayerBufferingCallback (OnBuffering);
+            viz_data_callback = new BansheePlayerVizDataCallback (OnVizData);
             tag_found_callback = new GstTaggerTagFoundCallback (OnTagFound);
             
             bp_set_eos_callback (handle, eos_callback);
@@ -108,6 +111,7 @@
             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_viz_data_callback (handle, viz_data_callback);
             bp_set_tag_found_callback (handle, tag_found_callback);
             
             OnStateChanged (PlayerEngineState.Ready);
@@ -254,7 +258,33 @@
         {
             OnTagFound (ProcessNativeTagResult (tagName, ref value));
         }
+        
+        private void OnVizData (IntPtr player, int channels, int samples, IntPtr data)
+        {
+            if (this.mDataAvailable == null)
+                return;
             
+            float[] flat = new float[channels * samples];
+            
+            System.Runtime.InteropServices.Marshal.Copy (data, flat, 0, flat.Length);
+            
+            float[][] cbd = new float[channels][];
+            
+            int offset = 0;
+            
+            for (int i = 0; i < channels; i++) {
+                float[] channel = new float[samples];
+                for (int j = 0; j < samples; j++) {
+                    channel[j] = flat[offset + j * channels];
+                }
+                
+                cbd[i] = channel;
+                offset++;
+            }
+            
+            this.mDataAvailable (cbd);
+        }
+        
         private static StreamTag ProcessNativeTagResult (string tagName, ref GLib.Value valueRaw)
         {
             if (tagName == String.Empty || tagName == null) {
@@ -390,6 +420,13 @@
             get { return decoder_capabilities; }
         }
         
+        private event VisualizationDataCallback mDataAvailable = null;
+        
+        public event VisualizationDataCallback DataAvailable {
+            add { this.mDataAvailable += value; }
+            remove { this.mDataAvailable -= value; }
+        }
+        
         [DllImport ("libbanshee")]
         private static extern IntPtr bp_new ();
         
@@ -403,6 +440,9 @@
         private static extern void bp_set_error_callback (HandleRef player, BansheePlayerErrorCallback cb);
         
         [DllImport ("libbanshee")]
+        private static extern void bp_set_viz_data_callback(HandleRef player, BansheePlayerVizDataCallback cb);
+        
+        [DllImport ("libbanshee")]
         private static extern void bp_set_state_changed_callback (HandleRef player, 
             BansheePlayerStateChangedCallback cb);
             
Index: libbanshee/banshee-player.c
===================================================================
--- libbanshee/banshee-player.c	(revision 3796)
+++ libbanshee/banshee-player.c	(working copy)
@@ -338,3 +338,9 @@
     *resource = GST_RESOURCE_ERROR;
     *stream = GST_STREAM_ERROR;
 }
+
+P_INVOKE void
+bp_set_viz_data_callback (BansheePlayer *player, BansheePlayerVizDataCallback cb)
+{
+    SET_CALLBACK (viz_data_cb);
+}
Index: libbanshee/banshee-player-pipeline.c
===================================================================
--- libbanshee/banshee-player-pipeline.c	(revision 3796)
+++ libbanshee/banshee-player-pipeline.c	(working copy)
@@ -31,6 +31,16 @@
 #include "banshee-player-video.h"
 #include "banshee-player-missing-elements.h"
 
+
+static GstStaticCaps viz_data_sink_caps = GST_STATIC_CAPS(
+        "audio/x-raw-float, "
+        /* "rate = (int) [1, MAX], " */
+        "rate = (int) 30720, "
+        /* "channels = (int) [1, MAX], " */
+        "channels = (int) 2, "
+        "endianness = (int) BYTE_ORDER, "
+        "width = (int) 32");
+
 // ---------------------------------------------------------------------------
 // Private Functions
 // ---------------------------------------------------------------------------
@@ -163,6 +173,67 @@
     return TRUE;
 }
 
+static void
+bp_viz_callback (GstElement *sink, GstBuffer *buffer, GstPad *pad, gpointer user_data)
+{
+    BansheePlayer *player = (BansheePlayer*) user_data;
+    
+    int channels, wanted_size;
+    float *data;
+    GstStructure *structure;
+    
+    if (player->viz_data_cb == NULL)
+        return;
+    
+    structure = gst_caps_get_structure (gst_buffer_get_caps (buffer), 0);
+    gst_structure_get_int (structure, "channels", &channels);
+    
+    wanted_size = channels * 512 * sizeof(float);
+    
+    gst_adapter_push (player->viz_buffer, gst_buffer_copy (buffer));
+    
+    while ((data = (float*) gst_adapter_peek (player->viz_buffer, wanted_size)) != NULL) {
+        player->viz_data_cb (player, channels, 512, data);
+        gst_adapter_flush (player->viz_buffer, wanted_size);
+    }
+}
+
+static void
+bp_construct_viz_pipeline (BansheePlayer *player)
+{
+    GstElement *fake, *converter, *resampler, *audiosinkqueue;
+    GstCaps *caps;
+    GstPad *pad;
+    
+    player->viz_buffer = gst_adapter_new ();
+    
+    fake = gst_element_factory_make ("fakesink", NULL);
+    g_signal_connect (G_OBJECT (fake), "handoff", G_CALLBACK (bp_viz_callback), player);
+    g_object_set (G_OBJECT (fake), "signal-handoffs", TRUE, "sync", TRUE, NULL);
+    gst_bin_add (GST_BIN (player->audiobin), fake);
+    
+    converter = gst_element_factory_make("audioconvert", NULL);
+    gst_bin_add (GST_BIN (player->audiobin), converter);
+    
+    resampler = gst_element_factory_make("audioresample", NULL);
+    gst_bin_add (GST_BIN (player->audiobin), resampler);
+    
+    audiosinkqueue = gst_element_factory_make("queue", NULL);
+    gst_bin_add (GST_BIN (player->audiobin), audiosinkqueue);
+    
+    gst_pad_link (gst_element_get_request_pad (player->audiotee, "src%d"),
+        pad = gst_element_get_static_pad (audiosinkqueue, "sink"));
+    
+    gst_object_unref (GST_OBJECT (pad));
+    
+    caps = gst_static_caps_get (&viz_data_sink_caps);
+    gst_element_link (audiosinkqueue, resampler);
+    gst_element_link (resampler, converter);
+    gst_element_link_filtered (converter, fake, caps);
+    
+    gst_caps_unref (caps);
+}
+
 // ---------------------------------------------------------------------------
 // Internal Functions
 // ---------------------------------------------------------------------------
@@ -248,6 +319,8 @@
     
     gst_element_link (audiosinkqueue, audiosink);
     
+    bp_construct_viz_pipeline (player);
+    
     // Now that our internal audio sink is constructed, tell playbin to use it
     g_object_set (G_OBJECT (player->playbin), "audio-sink", player->audiobin, NULL);
     
@@ -277,5 +350,10 @@
         gst_object_unref (GST_OBJECT(player->playbin));
     }
     
+    if (player->viz_buffer != NULL) {
+        gst_object_unref (player->viz_buffer);
+        player->viz_buffer = NULL;
+    }
+    
     player->playbin = NULL;
 }
Index: libbanshee/banshee-player-private.h
===================================================================
--- libbanshee/banshee-player-private.h	(revision 3796)
+++ libbanshee/banshee-player-private.h	(working copy)
@@ -35,6 +35,7 @@
 
 #include <string.h>
 #include <gst/gst.h>
+#include <gst/base/gstadapter.h>
 #include <gdk/gdk.h>
 
 #ifdef HAVE_GST_PBUTILS
@@ -64,6 +65,7 @@
 typedef void (* BansheePlayerIterateCallback)      (BansheePlayer *player);
 typedef void (* BansheePlayerBufferingCallback)    (BansheePlayer *player, gint buffering_progress);
 typedef void (* BansheePlayerTagFoundCallback)     (BansheePlayer *player, const gchar *tag, const GValue *value);
+typedef void (* BansheePlayerVizDataCallback)      (BansheePlayer *player, int channels, int samples, float data[]);
 
 struct BansheePlayer {
 
@@ -74,6 +76,7 @@
     BansheePlayerIterateCallback iterate_cb;
     BansheePlayerBufferingCallback buffering_cb;
     BansheePlayerTagFoundCallback tag_found_cb;
+    BansheePlayerVizDataCallback viz_data_cb;
 
     // Pipeline Elements
     GstElement *playbin;
@@ -95,6 +98,9 @@
     GdkWindow *video_window;
     #endif
     
+    // Visualization buffer
+    GstAdapter *viz_buffer;
+    
     // Plugin Installer State
     GdkWindow *window;
     GSList *missing_element_details;
