2006-08-30 Jorn Baayen <jorn@openedhand.com>
authorJorn Baayen <jorn@openedhand.com>
Wed, 30 Aug 2006 09:06:35 +0000 (09:06 +0000)
committerJorn Baayen <jorn@openedhand.com>
Wed, 30 Aug 2006 09:06:35 +0000 (09:06 +0000)
* clutter/Makefile.am:
* clutter/clutter-audio.c:
* clutter/clutter-audio.h:
* clutter/clutter.h:

Added ClutterAudio audio playback object.

ChangeLog
clutter/Makefile.am
clutter/clutter-audio.c [new file with mode: 0644]
clutter/clutter-audio.h [new file with mode: 0644]
clutter/clutter.h

index d89d0e3..18ed768 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2006-08-30  Jorn Baayen  <jorn@openedhand.com>
+
+       * clutter/Makefile.am:
+       * clutter/clutter-audio.c:
+       * clutter/clutter-audio.h:
+       * clutter/clutter.h:
+
+       Added ClutterAudio audio playback object.
+
 2006-08-29  Matthew Allum  <mallum@openedhand.com>
 
        * clutter/clutter-fixed.h:
index b88ff8e..e7772b1 100644 (file)
@@ -25,6 +25,7 @@ source_h =                              \
        $(srcdir)/clutter-behaviour.h     \
        $(srcdir)/clutter-behaviours.h    \
        $(srcdir)/clutter-alpha.h         \
+       $(srcdir)/clutter-audio.h         \
         $(srcdir)/clutter-main.h          
 
 clutter-marshal.h: clutter-marshal.list
@@ -90,6 +91,7 @@ source_c = clutter-main.c          \
            clutter-behaviour.c    \
            clutter-behaviours.c           \
           clutter-alpha.c         \
+          clutter-audio.c         \
           clutter-enum-types.c
 
 source_h_priv = clutter-private.h
diff --git a/clutter/clutter-audio.c b/clutter/clutter-audio.c
new file mode 100644 (file)
index 0000000..df595eb
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum@openedhand.com>
+ *             Jorn Baayen  <jorn@openedhand.com>
+ *
+ * Copyright (C) 2006 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:clutter-audio
+ * @short_description: Object for playback of audio files.
+ *
+ * #ClutterAudio is an object that plays audio files.
+ */
+
+#include "clutter-audio.h"
+#include "clutter-main.h"
+#include "clutter-private.h"   /* for DBG */
+#include "clutter-marshal.h"
+
+#include <gst/gst.h>
+#include <gst/audio/gstbaseaudiosink.h>
+
+#include <glib.h>
+
+struct _ClutterAudioPrivate
+{
+  GstElement *playbin;
+  char       *uri;
+  gboolean    can_seek;
+  int         buffer_percent;
+  int         duration;
+  guint       tick_timeout_id;
+};
+
+enum {
+  PROP_0,
+  /* ClutterMedia proprs */
+  PROP_URI,
+  PROP_PLAYING,
+  PROP_POSITION,
+  PROP_VOLUME,
+  PROP_CAN_SEEK,
+  PROP_BUFFER_PERCENT,
+  PROP_DURATION
+};
+
+
+#define TICK_TIMEOUT 0.5
+
+static void clutter_media_init (ClutterMediaInterface *iface);
+
+static gboolean tick_timeout (ClutterAudio *audio);
+
+G_DEFINE_TYPE_EXTENDED (ClutterAudio,                          \
+                       clutter_audio,                        \
+                       G_TYPE_OBJECT,                         \
+                       0,                                            \
+                       G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MEDIA,    \
+                                              clutter_media_init));
+
+/* Interface implementation */
+
+static void
+set_uri (ClutterMedia *media,
+        const char   *uri)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+  GstState                    state, pending;
+
+  g_return_if_fail (CLUTTER_IS_AUDIO (audio));
+
+  priv = audio->priv;
+
+  if (!priv->playbin)
+    return;
+
+  g_free (priv->uri);
+
+  if (uri) 
+    {
+      priv->uri = g_strdup (uri);
+    
+      /**
+       * Ensure the tick timeout is installed.
+       * 
+       * We also have it installed in PAUSED state, because
+       * seeks etc may have a delayed effect on the position.
+       **/
+    if (priv->tick_timeout_id == 0) 
+      {
+       priv->tick_timeout_id = g_timeout_add (TICK_TIMEOUT * 1000,
+                                              (GSourceFunc) tick_timeout,
+                                              audio);
+      }
+    } 
+  else 
+    {
+      priv->uri = NULL;
+    
+      if (priv->tick_timeout_id > 0) 
+       {
+         g_source_remove (priv->tick_timeout_id);
+         priv->tick_timeout_id = 0;
+       }
+    }
+  
+  priv->can_seek = FALSE;
+  priv->duration = 0;
+
+  gst_element_get_state (priv->playbin, &state, &pending, 0);
+
+  if (pending)
+    state = pending;
+  
+  gst_element_set_state (priv->playbin, GST_STATE_NULL);
+  
+  g_object_set (priv->playbin,
+               "uri", uri,
+               NULL);
+  
+  /**
+   * Restore state.
+   **/
+  if (uri) 
+    gst_element_set_state (priv->playbin, state);
+  
+  /*
+   * Emit notififications for all these to make sure UI is not showing
+   * any properties of the old URI.
+   */
+  g_object_notify (G_OBJECT (audio), "uri");
+  g_object_notify (G_OBJECT (audio), "can-seek");
+  g_object_notify (G_OBJECT (audio), "duration");
+  g_object_notify (G_OBJECT (audio), "position");
+  
+}
+
+static const char *
+get_uri (ClutterMedia *media)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), NULL);
+
+  priv = audio->priv;
+
+  return priv->uri;
+}
+
+static void
+set_playing (ClutterMedia *media,
+            gboolean      playing)
+{
+  ClutterAudio         *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+
+  g_return_if_fail (CLUTTER_IS_AUDIO (audio));
+
+  priv = audio->priv;
+
+  if (!priv->playbin)
+    return;
+        
+  if (priv->uri) 
+    {
+      GstState state;
+    
+      if (playing)
+       state = GST_STATE_PLAYING;
+      else
+       state = GST_STATE_PAUSED;
+    
+      gst_element_set_state (audio->priv->playbin, state);
+    } 
+  else 
+    {
+      if (playing)
+       g_warning ("Tried to play, but no URI is loaded.");
+    }
+  
+  g_object_notify (G_OBJECT (audio), "playing");
+  g_object_notify (G_OBJECT (audio), "position");
+}
+
+static gboolean
+get_playing (ClutterMedia *media)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+  GstState                    state, pending;
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), FALSE);
+       
+  priv = audio->priv;
+
+  if (!priv->playbin)
+    return FALSE;
+  
+  gst_element_get_state (priv->playbin, &state, &pending, 0);
+  
+  if (pending)
+    return (pending == GST_STATE_PLAYING);
+  else
+    return (state == GST_STATE_PLAYING);
+}
+
+static void
+set_position (ClutterMedia *media,
+             int           position) /* seconds */
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+  GstState                    state, pending;
+
+  g_return_if_fail (CLUTTER_IS_AUDIO (audio));
+
+  priv = audio->priv;
+
+  if (!priv->playbin)
+    return;
+
+  gst_element_get_state (priv->playbin, &state, &pending, 0);
+
+  if (pending)
+    state = pending;
+
+  gst_element_set_state (priv->playbin, GST_STATE_PAUSED);
+
+  gst_element_seek (priv->playbin,
+                   1.0,
+                   GST_FORMAT_TIME,
+                   GST_SEEK_FLAG_FLUSH,
+                   GST_SEEK_TYPE_SET,
+                   position * GST_SECOND,
+                   0, 0);
+
+  gst_element_set_state (priv->playbin, state);
+}
+
+static int
+get_position (ClutterMedia *media)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+  GstQuery                   *query;
+  gint64                      position;
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
+
+  priv = audio->priv;
+
+  if (!priv->playbin)
+    return -1;
+  
+  query = gst_query_new_position (GST_FORMAT_TIME);
+  
+  if (gst_element_query (priv->playbin, query)) 
+    gst_query_parse_position (query, NULL, &position);
+  else
+    position = 0;
+  
+  gst_query_unref (query);
+  
+  return (position / GST_SECOND);
+}
+
+static void
+set_volume (ClutterMedia *media,
+           double        volume)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+
+  g_return_if_fail (CLUTTER_IS_AUDIO (audio));
+  // g_return_if_fail (volume >= 0.0 && volume <= GST_VOL_MAX);
+
+  priv = audio->priv;
+  
+  if (!priv->playbin)
+    return;
+  
+  g_object_set (G_OBJECT (audio->priv->playbin),
+               "volume", volume,
+               NULL);
+  
+  g_object_notify (G_OBJECT (audio), "volume");
+}
+
+static double
+get_volume (ClutterMedia *media)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+  ClutterAudioPrivate *priv; 
+  double                      volume;
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), 0.0);
+
+  priv = audio->priv;
+  
+  if (!priv->playbin)
+    return 0.0;
+
+  g_object_get (priv->playbin,
+               "volume", &volume,
+               NULL);
+  
+  return volume;
+}
+
+static gboolean
+can_seek (ClutterMedia *media)
+{
+  ClutterAudio        *audio = CLUTTER_AUDIO(media);
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), FALSE);
+  
+  return audio->priv->can_seek;
+}
+
+static int
+get_buffer_percent (ClutterMedia *media)
+{
+  ClutterAudio         *audio = CLUTTER_AUDIO(media);
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
+  
+  return audio->priv->buffer_percent;
+}
+
+static int
+get_duration (ClutterMedia *media)
+{
+  ClutterAudio         *audio = CLUTTER_AUDIO(media);
+
+  g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
+
+  return audio->priv->duration;
+}
+
+static void
+clutter_media_init (ClutterMediaInterface *iface)
+{
+  iface->set_uri            = set_uri;
+  iface->get_uri            = get_uri;
+  iface->set_playing        = set_playing;
+  iface->get_playing        = get_playing;
+  iface->set_position       = set_position;
+  iface->get_position       = get_position;
+  iface->set_volume         = set_volume;
+  iface->get_volume         = get_volume;
+  iface->can_seek           = can_seek;
+  iface->get_buffer_percent = get_buffer_percent;
+  iface->get_duration       = get_duration;
+}
+
+static void
+clutter_audio_dispose (GObject *object)
+{
+  ClutterAudio        *self;
+  ClutterAudioPrivate *priv; 
+
+  self = CLUTTER_AUDIO(object); 
+  priv = self->priv;
+
+  /* FIXME: flush an errors off bus ? */
+  /* gst_bus_set_flushing (priv->bus, TRUE); */
+
+  if (priv->playbin) 
+    {
+      gst_element_set_state (priv->playbin, GST_STATE_NULL);
+      gst_object_unref (GST_OBJECT (priv->playbin));
+      priv->playbin = NULL;
+    }
+
+  if (priv->tick_timeout_id > 0) 
+    {
+      g_source_remove (priv->tick_timeout_id);
+      priv->tick_timeout_id = 0;
+    }
+
+  G_OBJECT_CLASS (clutter_audio_parent_class)->dispose (object);
+}
+
+static void
+clutter_audio_finalize (GObject *object)
+{
+  ClutterAudio        *self;
+  ClutterAudioPrivate *priv; 
+
+  self = CLUTTER_AUDIO(object); 
+  priv = self->priv;
+
+  if (priv->uri)
+    g_free(priv->uri);
+
+  G_OBJECT_CLASS (clutter_audio_parent_class)->finalize (object);
+}
+
+static void
+clutter_audio_set_property (GObject      *object, 
+                                   guint         property_id,
+                                   const GValue *value, 
+                                   GParamSpec   *pspec)
+{
+  ClutterAudio *audio;
+
+  audio = CLUTTER_AUDIO(object);
+
+  switch (property_id)
+    {
+    case PROP_URI:
+      clutter_media_set_uri (CLUTTER_MEDIA(audio), 
+                            g_value_get_string (value));
+      break;
+    case PROP_PLAYING:
+      clutter_media_set_playing (CLUTTER_MEDIA(audio),
+                                g_value_get_boolean (value));
+      break;
+    case PROP_POSITION:
+      clutter_media_set_position (CLUTTER_MEDIA(audio),
+                                 g_value_get_int (value));
+      break;
+    case PROP_VOLUME:
+      clutter_media_set_volume (CLUTTER_MEDIA(audio),
+                               g_value_get_double (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+clutter_audio_get_property (GObject    *object, 
+                                   guint       property_id,
+                                   GValue     *value, 
+                                   GParamSpec *pspec)
+{
+  ClutterAudio *audio;
+  ClutterMedia        *media;
+
+  audio = CLUTTER_AUDIO (object);
+  media         = CLUTTER_MEDIA (audio);
+
+  switch (property_id)
+    {
+    case PROP_URI:
+      g_value_set_string (value, clutter_media_get_uri (media));
+      break;
+    case PROP_PLAYING:
+      g_value_set_boolean (value, clutter_media_get_playing (media));
+      break;
+    case PROP_POSITION:
+      g_value_set_int (value, clutter_media_get_position (media));
+      break;
+    case PROP_VOLUME:
+      g_value_set_double (value, clutter_media_get_volume (media));
+      break;
+    case PROP_CAN_SEEK:
+      g_value_set_boolean (value, clutter_media_get_can_seek (media));
+      break;
+    case PROP_BUFFER_PERCENT:
+      g_value_set_int (value, clutter_media_get_buffer_percent (media));
+      break;
+    case PROP_DURATION:
+      g_value_set_int (value, clutter_media_get_duration (media));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+clutter_audio_class_init (ClutterAudioClass *klass)
+{
+  GObjectClass        *object_class;
+
+  object_class = (GObjectClass*)klass;
+
+  object_class->dispose      = clutter_audio_dispose;
+  object_class->finalize     = clutter_audio_finalize;
+  object_class->set_property = clutter_audio_set_property;
+  object_class->get_property = clutter_audio_get_property;
+
+  /* Interface props */
+
+  g_object_class_override_property (object_class, PROP_URI, "uri");
+  g_object_class_override_property (object_class, PROP_PLAYING, "playing");
+  g_object_class_override_property (object_class, PROP_POSITION, "position");
+  g_object_class_override_property (object_class, PROP_VOLUME, "volume");
+  g_object_class_override_property (object_class, PROP_CAN_SEEK, "can-seek");
+  g_object_class_override_property (object_class, PROP_DURATION, "duration");
+  g_object_class_override_property (object_class, PROP_BUFFER_PERCENT, 
+                                   "buffer-percent" );
+}
+
+static void
+bus_message_error_cb (GstBus            *bus,
+                      GstMessage        *message,
+                      ClutterAudio *audio)
+{
+  GError *error;
+
+  error = NULL;
+  gst_message_parse_error (message, &error, NULL);
+        
+  g_signal_emit_by_name (CLUTTER_MEDIA(audio), "error", error);
+
+  g_error_free (error);
+}
+
+static void
+bus_message_eos_cb (GstBus            *bus,
+                    GstMessage        *message,
+                    ClutterAudio *audio)
+{
+  g_object_notify (G_OBJECT (audio), "position");
+
+  g_signal_emit_by_name (CLUTTER_MEDIA(audio), "eos");
+}
+
+static void
+bus_message_tag_cb (GstBus            *bus,
+                    GstMessage        *message,
+                    ClutterAudio *audio)
+{
+  GstTagList *tag_list;
+
+  gst_message_parse_tag (message, &tag_list);
+
+  g_signal_emit_by_name (CLUTTER_MEDIA(audio), 
+                        "metadata-available", 
+                        tag_list);
+  
+  gst_tag_list_free (tag_list);
+}
+
+static void
+bus_message_buffering_cb (GstBus            *bus,
+                          GstMessage        *message,
+                          ClutterAudio *audio)
+{
+  const GstStructure *str;
+  
+  str = gst_message_get_structure (message);
+  if (!str)
+    return;
+
+  if (!gst_structure_get_int (str,
+                             "buffer-percent",
+                             &audio->priv->buffer_percent))
+    return;
+        
+  g_object_notify (G_OBJECT (audio), "buffer-percent");
+}
+
+static void
+bus_message_duration_cb (GstBus            *bus,
+                         GstMessage        *message,
+                         ClutterAudio *audio)
+{
+  GstFormat format;
+  gint64 duration;
+
+  gst_message_parse_duration (message,
+                             &format,
+                             &duration);
+
+  if (format != GST_FORMAT_TIME)
+    return;
+  
+  audio->priv->duration = duration / GST_SECOND;
+
+  g_object_notify (G_OBJECT (audio), "duration");
+}
+
+static void
+bus_message_state_change_cb (GstBus            *bus,
+                             GstMessage        *message,
+                             ClutterAudio *audio)
+{
+  gpointer src;
+  GstState old_state, new_state;
+
+  src = GST_MESSAGE_SRC (message);
+        
+  if (src != audio->priv->playbin)
+    return;
+
+  gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
+
+  if (old_state == GST_STATE_READY && 
+      new_state == GST_STATE_PAUSED) 
+    {
+      GstQuery *query;
+      
+      /**
+       * Determine whether we can seek.
+       **/
+      query = gst_query_new_seeking (GST_FORMAT_TIME);
+      
+      if (gst_element_query (audio->priv->playbin, query)) {
+       gst_query_parse_seeking (query,
+                                NULL,
+                                &audio->priv->can_seek,
+                                NULL,
+                                NULL);
+      } else {
+       /*
+        * Could not query for ability to seek. Determine
+        * using URI.
+        */
+       
+       if (g_str_has_prefix (audio->priv->uri,
+                             "http://")) {
+         audio->priv->can_seek = FALSE;
+       } else {
+         audio->priv->can_seek = TRUE;
+       }
+      }
+      
+      gst_query_unref (query);
+      
+      g_object_notify (G_OBJECT (audio), "can-seek");
+      
+      /**
+       * Determine the duration.
+       **/
+      query = gst_query_new_duration (GST_FORMAT_TIME);
+      
+      if (gst_element_query (audio->priv->playbin, query)) 
+       {
+         gint64 duration;
+         
+         gst_query_parse_duration (query, NULL, &duration);
+
+         audio->priv->duration = duration / GST_SECOND;
+                        
+         g_object_notify (G_OBJECT (audio), "duration");
+       }
+
+      gst_query_unref (query);
+    }
+}
+
+static gboolean
+tick_timeout (ClutterAudio *audio)
+{
+  g_object_notify (G_OBJECT (audio), "position");
+
+  return TRUE;
+}
+
+static void
+fakesink_handoff_cb (GstElement *fakesrc, 
+                    GstBuffer  *buffer,
+                    GstPad     *pad, 
+                    gpointer    user_data)
+{
+  GstStructure  *structure;
+  int            width, height;
+  GdkPixbuf     *pixb;
+
+  structure = gst_caps_get_structure(GST_CAPS(buffer->caps), 0);
+  gst_structure_get_int(structure, "width", &width);
+  gst_structure_get_int(structure, "height", &height);
+
+  /* FIXME: We really dont want to do this every time as gobject creation
+   *        really need a clutter_texture_set_from_data call ?
+  */
+  pixb = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
+                                  GDK_COLORSPACE_RGB, 
+                                  FALSE, 
+                                  8, 
+                                  width, 
+                                  height,
+                                  (3 * width + 3) &~ 3,
+                                  NULL,
+                                  NULL);
+  
+  if (pixb)
+    {
+      clutter_texture_set_pixbuf (CLUTTER_TEXTURE(user_data), pixb);
+      g_object_unref(G_OBJECT(pixb));
+    }
+}
+
+static gboolean
+lay_pipeline (ClutterAudio *audio)
+{
+  ClutterAudioPrivate *priv;
+  GstElement                 *audio_sink = NULL;
+
+  priv = audio->priv;
+
+  priv->playbin = gst_element_factory_make ("playbin", "playbin");
+
+  if (!priv->playbin) 
+    {
+      g_warning ("Unable to create playbin GST element.");
+      return FALSE;
+    }
+
+  audio_sink = gst_element_factory_make ("gconfaudiosink", "audio-sink");
+  if (!audio_sink) 
+    {
+      audio_sink = gst_element_factory_make ("autoaudiosink", "audio-sink");
+      if (!audio_sink) 
+       {
+         audio_sink = gst_element_factory_make ("alsasink", "audio-sink");
+         g_warning ("Could not create a GST audio_sink. "
+                    "Audio unavailable.");
+
+         if (!audio_sink)      /* Need to bother ? */
+           audio_sink = gst_element_factory_make ("fakesink", "audio-sink");
+       }
+    }
+
+  g_object_set (G_OBJECT (priv->playbin),
+               "audio-sink", audio_sink,
+               NULL);
+
+  return TRUE;
+}
+
+static void
+clutter_audio_init (ClutterAudio *audio)
+{
+  ClutterAudioPrivate *priv;
+  GstBus                     *bus;
+
+  priv                 = g_new0 (ClutterAudioPrivate, 1);
+  audio->priv  = priv;
+
+  if (!lay_pipeline(audio))
+    {
+      g_warning("Failed to initiate suitable playback pipeline.");
+      return;
+    }
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->playbin));
+
+  gst_bus_add_signal_watch (bus);
+
+  g_signal_connect_object (bus,
+                          "message::error",
+                          G_CALLBACK (bus_message_error_cb),
+                          audio,
+                          0);
+
+  g_signal_connect_object (bus,
+                          "message::eos",
+                          G_CALLBACK (bus_message_eos_cb),
+                          audio,
+                          0);
+
+  g_signal_connect_object (bus,
+                          "message::tag",
+                          G_CALLBACK (bus_message_tag_cb),
+                          audio,
+                          0);
+
+  g_signal_connect_object (bus,
+                          "message::buffering",
+                          G_CALLBACK (bus_message_buffering_cb),
+                          audio,
+                          0);
+
+  g_signal_connect_object (bus,
+                          "message::duration",
+                          G_CALLBACK (bus_message_duration_cb),
+                          audio,
+                          0);
+
+  g_signal_connect_object (bus,
+                          "message::state-changed",
+                          G_CALLBACK (bus_message_state_change_cb),
+                          audio,
+                          0);
+
+  gst_object_unref (GST_OBJECT (bus));
+
+  return;
+}
+
+/**
+ * clutter_audio_new:
+ *
+ * Creates #ClutterAudio object.
+ *
+ * Return value: A newly allocated #ClutterAudio object.
+ */
+ClutterAudio*
+clutter_audio_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_AUDIO, NULL);
+}
+
diff --git a/clutter/clutter-audio.h b/clutter/clutter-audio.h
new file mode 100644 (file)
index 0000000..99914d4
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum@openedhand.com>
+ *             Jorn Baayen  <jorn@openedhand.com>
+ *
+ * Copyright (C) 2006 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _HAVE_CLUTTER_AUDIO_H
+#define _HAVE_CLUTTER_AUDIO_H
+
+#include <glib-object.h>
+#include <clutter/clutter-media.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_AUDIO clutter_audio_get_type()
+
+#define CLUTTER_AUDIO(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  CLUTTER_TYPE_AUDIO, ClutterAudio))
+
+#define CLUTTER_AUDIO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  CLUTTER_TYPE_AUDIO, ClutterAudioClass))
+
+#define CLUTTER_IS_AUDIO(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  CLUTTER_TYPE_AUDIO))
+
+#define CLUTTER_IS_AUDIO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  CLUTTER_TYPE_AUDIO))
+
+#define CLUTTER_AUDIO_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  CLUTTER_TYPE_AUDIO, ClutterAudioClass))
+
+typedef struct _ClutterAudio        ClutterAudio;
+typedef struct _ClutterAudioClass   ClutterAudioClass;
+typedef struct _ClutterAudioPrivate ClutterAudioPrivate;
+
+/* #define CLUTTER_AUDIO_ERROR clutter_audio_error_quark() */
+
+struct _ClutterAudio
+{
+  GObject              parent;
+  ClutterAudioPrivate *priv;
+}; 
+
+struct _ClutterAudioClass 
+{
+  GObjectClass parent_class;
+
+  /* Future padding */
+  void (* _clutter_reserved1) (void);
+  void (* _clutter_reserved2) (void);
+  void (* _clutter_reserved3) (void);
+  void (* _clutter_reserved4) (void);
+  void (* _clutter_reserved5) (void);
+  void (* _clutter_reserved6) (void);
+}; 
+
+GType         clutter_audio_get_type (void) G_GNUC_CONST;
+ClutterAudio *clutter_audio_new      (void);
+
+G_END_DECLS
+
+#endif
index c1f00d9..164bd01 100644 (file)
@@ -19,6 +19,7 @@
 #include "clutter-video-texture.h"
 #include "clutter-label.h"
 #include "clutter-alpha.h"
+#include "clutter-audio.h"
 #include "clutter-enum-types.h"
 
 #endif