[the-board] [tb] Add initial code for TbSoundPlayer and TbSoundRecorder



commit 68ed4061de84ecf3c90a6a6a569ce3896bdfa0ff
Author: Lucas Rocha <lucasr gnome org>
Date:   Sun Dec 26 00:19:00 2010 +0000

    [tb] Add initial code for TbSoundPlayer and TbSoundRecorder
    
    They will be used on the implementation of SoundThing.

 src/Makefile-tb.am         |    8 +-
 src/tb/tb-sound-player.c   |  690 ++++++++++++++++++++++++++++++++++++++++++++
 src/tb/tb-sound-player.h   |   44 +++
 src/tb/tb-sound-recorder.c |  389 +++++++++++++++++++++++++
 src/tb/tb-sound-recorder.h |   47 +++
 5 files changed, 1176 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile-tb.am b/src/Makefile-tb.am
index 69d7811..55d6213 100644
--- a/src/Makefile-tb.am
+++ b/src/Makefile-tb.am
@@ -23,7 +23,9 @@ tb_source_h = \
     tb/tb-gio-util.h \
     tb/tb-gdk-util.h \
     tb/tb-gobject-util.h \
-    tb/tb-mx-util.h
+    tb/tb-mx-util.h \
+    tb/tb-sound-player.h \
+    tb/tb-sound-recorder.h
 
 if HAVE_LIBSOUP
 tb_source_h += tb/tb-soup-util.h
@@ -34,7 +36,9 @@ tb_source_c = \
     tb/tb-gio-util.c \
     tb/tb-gdk-util.c \
     tb/tb-gobject-util.c \
-    tb/tb-mx-util.c
+    tb/tb-mx-util.c \
+    tb/tb-sound-player.c \
+    tb/tb-sound-recorder.c
 
 if HAVE_LIBSOUP
 tb_source_c += tb/tb-soup-util.c
diff --git a/src/tb/tb-sound-player.c b/src/tb/tb-sound-player.c
new file mode 100644
index 0000000..9103576
--- /dev/null
+++ b/src/tb/tb-sound-player.c
@@ -0,0 +1,690 @@
+#include <glib.h>
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "tb-enum-types.h"
+#include "tb/tb-sound-player.h"
+
+G_DEFINE_TYPE (TbSoundPlayer, tb_sound_player, G_TYPE_OBJECT);
+
+#define TB_SOUND_PLAYER_GET_PRIVATE(obj)    \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TB_TYPE_SOUND_PLAYER, TbSoundPlayerPrivate))
+
+#define TICK_TIMEOUT 0.5
+
+enum
+{
+  PROP_0,
+
+  PROP_PLAYING,
+  PROP_STATE,
+  PROP_PROGRESS,
+  PROP_DURATION,
+  PROP_FILENAME
+};
+
+struct _TbSoundPlayerPrivate
+{
+  GstElement            *pipeline;
+  GstBus                *bus;
+  TbSoundPlayerState     state;
+  char                  *filename;
+  gboolean               playing;
+  GstState               stacked_state;
+  gdouble                stacked_progress;
+  gdouble                target_progress;
+  gdouble                duration;
+  guint                  tick_timeout_id;
+
+  guint                  in_seek : 1;
+};
+
+static void tb_sound_player_destroy_pipeline (TbSoundPlayer *player);
+
+static void
+tb_sound_player_set_state (TbSoundPlayer      *player,
+                           TbSoundPlayerState  state)
+{
+  TbSoundPlayerPrivate *priv;
+
+  g_return_if_fail (TB_IS_SOUND_PLAYER (player));
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (priv->state == state)
+    return;
+
+  priv->state = state;
+
+  g_object_notify (G_OBJECT (player), "state");
+}
+
+static void
+tb_sound_player_set_filename (TbSoundPlayer *player,
+                              const char    *filename)
+{
+  TbSoundPlayerPrivate *priv;
+
+  g_return_if_fail (TB_IS_SOUND_PLAYER (player));
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (priv->filename &&
+      !strcmp (priv->filename, filename))
+    return;
+
+  g_free (priv->filename);
+  priv->filename = g_strdup (filename);
+
+  if (priv->pipeline)
+    tb_sound_player_destroy_pipeline (player);
+
+  g_object_notify (G_OBJECT (player), "filename");
+}
+
+static void
+tb_sound_player_set_progress (TbSoundPlayer *player,
+                              gdouble        progress)
+{
+  TbSoundPlayerPrivate *priv;
+  GstState pending;
+  GstQuery *duration_q;
+  gint64 position;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (!priv->pipeline)
+    return;
+
+  priv->target_progress = progress;
+
+  if (priv->in_seek)
+    {
+      priv->stacked_progress = progress;
+      return;
+    }
+
+  gst_element_get_state (priv->pipeline, &priv->stacked_state, &pending, 0);
+
+  if (pending)
+    priv->stacked_state = pending;
+
+  gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+
+  duration_q = gst_query_new_duration (GST_FORMAT_TIME);
+
+  if (gst_element_query (priv->pipeline, duration_q))
+    {
+      gint64 duration = 0;
+
+      gst_query_parse_duration (duration_q, NULL, &duration);
+
+      position = progress * duration;
+    }
+  else
+    position = 0;
+
+  gst_query_unref (duration_q);
+
+  gst_element_seek (priv->pipeline,
+		    1.0,
+		    GST_FORMAT_TIME,
+		    GST_SEEK_FLAG_FLUSH,
+		    GST_SEEK_TYPE_SET,
+		    position,
+		    GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+
+  priv->in_seek = TRUE;
+  priv->stacked_progress = 0.0;
+}
+
+static gdouble
+tb_sound_player_get_progress (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GstQuery *position_q, *duration_q;
+  gdouble progress;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (!priv->pipeline)
+    return 0.0;
+
+  if (priv->in_seek)
+    {
+      return priv->target_progress;
+    }
+
+  position_q = gst_query_new_position (GST_FORMAT_TIME);
+  duration_q = gst_query_new_duration (GST_FORMAT_TIME);
+
+  if (gst_element_query (priv->pipeline, position_q) &&
+      gst_element_query (priv->pipeline, duration_q))
+    {
+      gint64 position, duration;
+
+      position = duration = 0;
+
+      gst_query_parse_position (position_q, NULL, &position);
+      gst_query_parse_duration (duration_q, NULL, &duration);
+
+      progress = CLAMP ((gdouble) position / (gdouble) duration, 0.0, 1.0);
+    }
+  else
+    progress = 0.0;
+
+  gst_query_unref (position_q);
+  gst_query_unref (duration_q);
+
+  return progress;
+}
+
+static void
+tb_sound_player_query_duration (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GstFormat format = GST_FORMAT_TIME;
+  gdouble new_duration, difference;
+  gboolean success;
+  gint64 duration;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  success = gst_element_query_duration (priv->pipeline, &format, &duration);
+
+  if (G_UNLIKELY (success != TRUE))
+    return;
+
+  new_duration = (gdouble) duration / GST_SECOND;
+
+  difference = ABS (priv->duration - new_duration);
+
+  if (difference > 1e-3)
+    {
+      priv->duration = new_duration;
+
+      if (difference > 1.0)
+        g_object_notify (G_OBJECT (player), "duration");
+    }
+}
+
+static void
+tb_sound_player_reset_pipeline (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GstState state, pending;
+  GstMessage *msg;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (!priv->pipeline)
+    return;
+
+  gst_element_get_state (priv->pipeline, &state, &pending, 0);
+
+  if (state == GST_STATE_NULL && pending == GST_STATE_VOID_PENDING)
+    {
+      return;
+    }
+  else if (state == GST_STATE_NULL && pending != GST_STATE_VOID_PENDING)
+    {
+      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+      return;
+    }
+
+  gst_element_set_state (priv->pipeline, GST_STATE_READY);
+  gst_element_get_state (priv->pipeline, NULL, NULL, -1);
+
+  while ((msg = gst_bus_pop (priv->bus)))
+    gst_bus_async_signal_func (priv->bus, msg, NULL);
+
+  gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+
+  priv->duration = 0;
+
+  g_object_notify (G_OBJECT (player), "duration");
+  g_object_notify (G_OBJECT (player), "progress");
+}
+
+static void
+tb_sound_player_destroy_pipeline (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (priv->bus)
+    {
+      gst_bus_set_flushing (priv->bus, TRUE);
+      gst_bus_remove_signal_watch (priv->bus);
+
+      gst_object_unref (priv->bus);
+      priv->bus = NULL;
+    }
+
+  if (priv->pipeline)
+    {
+      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+
+      gst_object_unref (priv->pipeline);
+      priv->pipeline = NULL;
+    }
+
+  if (priv->tick_timeout_id != 0)
+    {
+      g_source_remove (priv->tick_timeout_id);
+      priv->tick_timeout_id = 0;
+    }
+
+  g_object_notify (G_OBJECT (player), "duration");
+  g_object_notify (G_OBJECT (player), "progress");
+}
+
+static void
+tb_sound_player_on_state_changed (GstBus        *bus,
+                                  GstMessage    *msg,
+                                  TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GstState state, old_state;
+
+  g_return_if_fail (TB_IS_SOUND_PLAYER (player));
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (msg->src != GST_OBJECT (priv->pipeline))
+    return;
+
+  gst_message_parse_state_changed (msg, &old_state, &state, NULL);
+
+  if (state == GST_STATE_PAUSED && old_state == GST_STATE_READY)
+    tb_sound_player_query_duration (player);
+
+  switch (state)
+    {
+    case GST_STATE_PLAYING:
+      tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_PLAYING);
+      break;
+
+    case GST_STATE_READY:
+    case GST_STATE_PAUSED:
+      tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_IDLE);
+      break;
+
+    default:
+      /* Do nothing */
+      break;
+    }
+}
+
+static void
+tb_sound_player_on_error (GstBus        *bus,
+                          GstMessage    *msg,
+                          TbSoundPlayer *player)
+{
+  tb_sound_player_reset_pipeline (player);
+  tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_ERROR);
+}
+
+static void
+tb_sound_player_on_eos (GstBus        *bus,
+                        GstMessage    *msg,
+                        TbSoundPlayer *player)
+{
+  g_object_notify (G_OBJECT (player), "progress");
+
+  tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_DONE);
+  tb_sound_player_reset_pipeline (player);
+}
+
+static void
+tb_sound_player_on_async_done (GstBus        *bus,
+                               GstMessage    *msg,
+                               TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (priv->in_seek)
+    {
+      g_object_notify (G_OBJECT (player), "progress");
+
+      priv->in_seek = FALSE;
+      gst_element_set_state (priv->pipeline, priv->stacked_state);
+
+      if (priv->stacked_progress)
+        {
+          tb_sound_player_set_progress (player, priv->stacked_progress);
+        }
+    }
+}
+
+static void
+tb_sound_player_on_duration (GstBus        *bus,
+                             GstMessage    *msg,
+                             TbSoundPlayer *player)
+{
+  gint64 duration;
+
+  gst_message_parse_duration (msg, NULL, &duration);
+
+  if (G_UNLIKELY (duration != GST_CLOCK_TIME_NONE))
+    return;
+
+  tb_sound_player_query_duration (player);
+}
+
+static gboolean
+tb_sound_player_tick_timeout (gpointer user_data)
+{
+  GObject *player = user_data;
+
+  g_object_notify (player, "progress");
+
+  return TRUE;
+}
+
+static gboolean
+tb_sound_player_ensure_pipeline (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GError *error;
+  gchar *pipeline_desc;
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (priv->pipeline)
+    return TRUE;
+
+  if (priv->filename == NULL)
+    {
+      tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_ERROR);
+      return FALSE;
+    }
+
+  error = NULL;
+
+  pipeline_desc = g_strdup_printf("playbin uri=file://%s",
+                                  priv->filename);
+
+  priv->pipeline = gst_parse_launch (pipeline_desc, &error);
+
+  g_free (pipeline_desc);
+
+  if (error)
+    {
+      g_error_free (error);
+      priv->pipeline = NULL;
+
+      tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_ERROR);
+      return FALSE;
+    }
+
+  if (!gst_element_set_state (priv->pipeline, GST_STATE_READY))
+    {
+      g_object_unref (priv->pipeline);
+      priv->pipeline = NULL;
+
+      tb_sound_player_set_state (player, TB_SOUND_PLAYER_STATE_ERROR);
+      return FALSE;
+    }
+
+  priv->bus = gst_element_get_bus (priv->pipeline);
+
+  gst_bus_add_signal_watch (priv->bus);
+
+  g_signal_connect (priv->bus,
+                    "message::state-changed",
+                    G_CALLBACK (tb_sound_player_on_state_changed),
+                    player);
+
+  g_signal_connect (priv->bus,
+                    "message::error",
+                    G_CALLBACK (tb_sound_player_on_error),
+                    player);
+
+  g_signal_connect (priv->bus,
+                    "message::eos",
+                    G_CALLBACK (tb_sound_player_on_eos),
+                    player);
+
+  g_signal_connect (priv->bus,
+                    "message::async-done",
+                    G_CALLBACK (tb_sound_player_on_async_done),
+                    player);
+
+  g_signal_connect (priv->bus,
+                    "message::duration",
+                    G_CALLBACK (tb_sound_player_on_duration),
+                    player);
+
+  if (priv->tick_timeout_id == 0)
+    {
+      priv->tick_timeout_id =
+        g_timeout_add (TICK_TIMEOUT * 1000,
+                       tb_sound_player_tick_timeout,
+                       player);
+    }
+
+  return TRUE;
+}
+
+void
+tb_sound_player_set_playing (TbSoundPlayer *player,
+                             gboolean       playing)
+{
+  TbSoundPlayerPrivate *priv;
+  GstState state;
+
+  g_return_if_fail (TB_IS_SOUND_PLAYER (player));
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (playing)
+    state = GST_STATE_PLAYING;
+  else
+    state = GST_STATE_PAUSED;
+
+  if (tb_sound_player_ensure_pipeline (player))
+    gst_element_set_state (priv->pipeline, state);
+
+  g_object_notify (G_OBJECT (player), "playing");
+  g_object_notify (G_OBJECT (player), "progress");
+}
+
+static gboolean
+tb_sound_player_get_playing (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+  GstState state, pending;
+  gboolean playing;
+
+  g_return_if_fail (TB_IS_SOUND_PLAYER (player));
+
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  if (!priv->pipeline)
+    return FALSE;
+
+  gst_element_get_state (priv->pipeline, &state, &pending, 0);
+
+  if (pending)
+    playing = (pending == GST_STATE_PLAYING);
+  else
+    playing = (state == GST_STATE_PLAYING);
+
+  return playing;
+}
+
+static void
+tb_sound_player_finalize (GObject *gobject)
+{
+  G_OBJECT_CLASS (tb_sound_player_parent_class)->finalize (gobject);
+}
+
+static void
+tb_sound_player_dispose (GObject *gobject)
+{
+  tb_sound_player_destroy_pipeline (TB_SOUND_PLAYER (gobject));
+
+  G_OBJECT_CLASS (tb_sound_player_parent_class)->dispose (gobject);
+}
+
+static void
+tb_sound_player_get_property (GObject    *gobject,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  TbSoundPlayer *player;
+  TbSoundPlayerPrivate *priv;
+  
+  player = TB_SOUND_PLAYER (gobject);
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  switch (prop_id)
+    {
+    case PROP_PLAYING:
+      g_value_set_boolean (value,
+                           tb_sound_player_get_playing (player));
+      break;
+
+    case PROP_STATE:
+      g_value_set_enum (value, priv->state);
+      break;
+
+    case PROP_PROGRESS:
+      g_value_set_double (value,
+                          tb_sound_player_get_progress (player));
+      break;
+
+    case PROP_DURATION:
+      g_value_set_double (value, priv->duration);
+      break;
+
+    case PROP_FILENAME:
+      g_value_set_string (value, priv->filename);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tb_sound_player_set_property (GObject      *gobject,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  TbSoundPlayer *player;
+  TbSoundPlayerPrivate *priv;
+  
+  player = TB_SOUND_PLAYER (gobject);
+  priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  switch (prop_id)
+    {
+    case PROP_PLAYING:
+      tb_sound_player_set_playing (player,
+                                   g_value_get_boolean (value));
+      break;
+
+    case PROP_PROGRESS:
+      tb_sound_player_set_progress (player,
+                                    g_value_get_double (value));
+      break;
+
+    case PROP_FILENAME:
+      tb_sound_player_set_filename (player,
+                                    g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tb_sound_player_class_init (TbSoundPlayerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TbSoundPlayerPrivate));
+
+  gobject_class->get_property = tb_sound_player_get_property;
+  gobject_class->set_property = tb_sound_player_set_property;
+  gobject_class->dispose = tb_sound_player_dispose;
+  gobject_class->finalize = tb_sound_player_finalize;
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PLAYING,
+                  g_param_spec_boolean ("playing",
+                                        "Playing",
+                                        "Whether player is playing or not",
+                                        FALSE,
+                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PROGRESS,
+                  g_param_spec_double ("progress",
+                                       "Progress",
+                                       "Player's playback progress",
+                                       0.0,
+                                       1.0,
+                                       0.0,
+                                       G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_DURATION,
+                  g_param_spec_double ("duration",
+                                       "Duration",
+                                       "Sound duration",
+                                       0.0,
+                                       G_MAXDOUBLE,
+                                       0.0,
+                                       G_PARAM_READABLE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_STATE,
+                  g_param_spec_enum ("state",
+                                     "State",
+                                     "State of the sound player",
+                                     TB_TYPE_SOUND_PLAYER_STATE,
+                                     TB_SOUND_PLAYER_STATE_UNKNOWN,
+                                     G_PARAM_READABLE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_FILENAME,
+                  g_param_spec_string ("filename",
+                                       "Filename",
+                                       "Filename to save sound to",
+                                       NULL,
+                                       G_PARAM_READWRITE |
+                                       G_PARAM_CONSTRUCT));
+}
+
+static void
+tb_sound_player_init (TbSoundPlayer *player)
+{
+  TbSoundPlayerPrivate *priv;
+
+  player->priv = TB_SOUND_PLAYER_GET_PRIVATE (player);
+
+  player->priv->state = TB_SOUND_PLAYER_STATE_UNKNOWN;
+  player->priv->playing = FALSE;
+  player->priv->filename = NULL;
+  player->priv->pipeline = NULL;
+  player->priv->bus = NULL;
+  player->priv->stacked_progress = 0.0;
+  player->priv->duration = 0.0;
+  player->priv->tick_timeout_id = 0;
+}
diff --git a/src/tb/tb-sound-player.h b/src/tb/tb-sound-player.h
new file mode 100644
index 0000000..5dd7459
--- /dev/null
+++ b/src/tb/tb-sound-player.h
@@ -0,0 +1,44 @@
+#ifndef __TB_SOUND_PLAYER_H__
+#define __TB_SOUND_PLAYER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TB_TYPE_SOUND_PLAYER            (tb_sound_player_get_type ())
+#define TB_SOUND_PLAYER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TB_TYPE_SOUND_PLAYER, TbSoundPlayer))
+#define TB_IS_SOUND_PLAYER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TB_TYPE_SOUND_PLAYER))
+#define TB_SOUND_PLAYER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  TB_TYPE_SOUND_PLAYER, TbSoundPlayerClass))
+#define TB_IS_SOUND_PLAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  TB_TYPE_SOUND_PLAYER))
+#define TB_SOUND_PLAYER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  TB_TYPE_SOUND_PLAYER, TbSoundPlayerClass))
+
+typedef struct _TbSoundPlayer          TbSoundPlayer;
+typedef struct _TbSoundPlayerPrivate   TbSoundPlayerPrivate;
+typedef struct _TbSoundPlayerClass     TbSoundPlayerClass;
+
+typedef enum
+{
+  TB_SOUND_PLAYER_STATE_UNKNOWN = 0,
+  TB_SOUND_PLAYER_STATE_IDLE    = 1,
+  TB_SOUND_PLAYER_STATE_PLAYING = 2,
+  TB_SOUND_PLAYER_STATE_DONE    = 3,
+  TB_SOUND_PLAYER_STATE_ERROR   = 4
+} TbSoundPlayerState;
+
+struct _TbSoundPlayer
+{
+  GObject parent_instance;
+
+  TbSoundPlayerPrivate *priv;
+};
+
+struct _TbSoundPlayerClass
+{
+  GObjectClass parent_class;
+};
+
+GType    tb_sound_player_get_type     (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __TB_SOUND_PLAYER_H__ */
diff --git a/src/tb/tb-sound-recorder.c b/src/tb/tb-sound-recorder.c
new file mode 100644
index 0000000..ec87aea
--- /dev/null
+++ b/src/tb/tb-sound-recorder.c
@@ -0,0 +1,389 @@
+#include <glib.h>
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "tb-enum-types.h"
+#include "tb/tb-sound-recorder.h"
+
+G_DEFINE_TYPE (TbSoundRecorder, tb_sound_recorder, G_TYPE_OBJECT);
+
+#define TB_SOUND_RECORDER_GET_PRIVATE(obj)    \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TB_TYPE_SOUND_RECORDER, TbSoundRecorderPrivate))
+
+enum
+{
+  PROP_0,
+
+  PROP_STATE,
+  PROP_FILENAME
+};
+
+struct _TbSoundRecorderPrivate
+{
+  GstElement            *pipeline;
+  GstBus                *bus;
+  TbSoundRecorderState   state;
+  char                  *filename;
+};
+
+static void tb_sound_recorder_destroy_pipeline (TbSoundRecorder *recorder);
+
+static void
+tb_sound_recorder_set_state (TbSoundRecorder      *recorder,
+                             TbSoundRecorderState  state)
+{
+  TbSoundRecorderPrivate *priv;
+
+  g_return_if_fail (TB_IS_SOUND_RECORDER (recorder));
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (priv->state == state)
+    return;
+
+  priv->state = state;
+
+  g_object_notify (G_OBJECT (recorder), "state");
+}
+
+static void
+tb_sound_recorder_set_filename (TbSoundRecorder *recorder,
+                                const char      *filename)
+{
+  TbSoundRecorderPrivate *priv;
+
+  g_return_if_fail (TB_IS_SOUND_RECORDER (recorder));
+  g_return_if_fail (filename != NULL);
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (priv->filename &&
+      !strcmp (priv->filename, filename))
+    return;
+
+  g_free (priv->filename);
+  priv->filename = g_strdup (filename);
+
+  if (priv->pipeline)
+    tb_sound_recorder_destroy_pipeline (recorder);
+
+  g_object_notify (G_OBJECT (recorder), "filename");
+}
+
+static void
+tb_sound_recorder_reset_pipeline (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+  GstState state, pending;
+  GstMessage *msg;
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (!priv->pipeline)
+    return;
+
+  gst_element_get_state (priv->pipeline, &state, &pending, 0);
+
+  if (state == GST_STATE_NULL && pending == GST_STATE_VOID_PENDING)
+    {
+      return;
+    }
+  else if (state == GST_STATE_NULL && pending != GST_STATE_VOID_PENDING)
+    {
+      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+      return;
+    }
+
+  gst_element_set_state (priv->pipeline, GST_STATE_READY);
+  gst_element_get_state (priv->pipeline, NULL, NULL, -1);
+
+  while ((msg = gst_bus_pop (priv->bus)))
+    gst_bus_async_signal_func (priv->bus, msg, NULL);
+
+  gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+}
+
+static void
+tb_sound_recorder_destroy_pipeline (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (priv->bus)
+    {
+      gst_bus_set_flushing (priv->bus, TRUE);
+      gst_bus_remove_signal_watch (priv->bus);
+
+      gst_object_unref (priv->bus);
+      priv->bus = NULL;
+    }
+
+  if (priv->pipeline)
+    {
+      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+
+      gst_object_unref (priv->pipeline);
+      priv->pipeline = NULL;
+    }
+}
+
+static void
+tb_sound_recorder_on_state_changed (GstBus          *bus,
+                                    GstMessage      *msg,
+                                    TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+  GstState state;
+
+  g_return_if_fail (TB_IS_SOUND_RECORDER (recorder));
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (msg->src != GST_OBJECT (priv->pipeline))
+    return;
+
+  gst_message_parse_state_changed (msg, NULL, &state, NULL);
+
+  switch (state)
+    {
+    case GST_STATE_PLAYING:
+      tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_RECORDING);
+      break;
+
+    case GST_STATE_READY:
+    case GST_STATE_PAUSED:
+      tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_IDLE);
+      break;
+
+    default:
+      /* Do nothing */
+      break;
+    }
+}
+
+static void
+tb_sound_recorder_on_error (GstBus          *bus,
+                            GstMessage      *msg,
+                            TbSoundRecorder *recorder)
+{
+  tb_sound_recorder_reset_pipeline (recorder);
+  tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_ERROR);
+}
+
+static void
+tb_sound_recorder_on_eos (GstBus          *bus,
+                          GstMessage      *msg,
+                          TbSoundRecorder *recorder)
+{
+  tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_DONE);
+  tb_sound_recorder_reset_pipeline (recorder);
+}
+
+static gboolean
+tb_sound_recorder_ensure_pipeline (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+  GError *error;
+  gchar *pipeline_desc;
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (priv->pipeline)
+    return TRUE;
+
+  if (priv->filename == NULL)
+    {
+      tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_ERROR);
+      return FALSE;
+    }
+
+  error = NULL;
+
+  pipeline_desc = g_strdup_printf("autoaudiosrc name=source ! "
+                                  "audioconvert ! "
+                                  "wavenc ! "
+                                  "filesink location=%s",
+                                  priv->filename);
+
+  priv->pipeline = gst_parse_launch (pipeline_desc, &error);
+
+  g_free (pipeline_desc);
+
+  if (error)
+    {
+      g_error_free (error);
+      priv->pipeline = NULL;
+
+      tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_ERROR);
+      return FALSE;
+    }
+
+  if (!gst_element_set_state (priv->pipeline, GST_STATE_READY))
+    {
+      g_object_unref (priv->pipeline);
+      priv->pipeline = NULL;
+
+      tb_sound_recorder_set_state (recorder, TB_SOUND_RECORDER_STATE_ERROR);
+      return FALSE;
+    }
+
+  priv->bus = gst_element_get_bus (priv->pipeline);
+
+  gst_bus_add_signal_watch (priv->bus);
+
+  g_signal_connect (priv->bus,
+                    "message::state-changed",
+                    G_CALLBACK (tb_sound_recorder_on_state_changed),
+                    recorder);
+
+  g_signal_connect (priv->bus,
+                    "message::error",
+                    G_CALLBACK (tb_sound_recorder_on_error),
+                    recorder);
+
+  g_signal_connect (priv->bus,
+                    "message::eos",
+                    G_CALLBACK (tb_sound_recorder_on_eos),
+                    recorder);
+
+  return TRUE;
+}
+
+static void
+tb_sound_recorder_finalize (GObject *gobject)
+{
+  G_OBJECT_CLASS (tb_sound_recorder_parent_class)->finalize (gobject);
+}
+
+static void
+tb_sound_recorder_dispose (GObject *gobject)
+{
+  tb_sound_recorder_destroy_pipeline (TB_SOUND_RECORDER (gobject));
+
+  G_OBJECT_CLASS (tb_sound_recorder_parent_class)->dispose (gobject);
+}
+
+static void
+tb_sound_recorder_get_property (GObject    *gobject,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  TbSoundRecorderPrivate *priv;
+  
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_STATE:
+      g_value_set_enum (value, priv->state);
+      break;
+
+    case PROP_FILENAME:
+      g_value_set_string (value, priv->filename);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tb_sound_recorder_set_property (GObject      *gobject,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  TbSoundRecorderPrivate *priv;
+  
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      tb_sound_recorder_set_filename (TB_SOUND_RECORDER (gobject),
+                                      g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tb_sound_recorder_class_init (TbSoundRecorderClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TbSoundRecorderPrivate));
+
+  gobject_class->get_property = tb_sound_recorder_get_property;
+  gobject_class->set_property = tb_sound_recorder_set_property;
+  gobject_class->dispose = tb_sound_recorder_dispose;
+  gobject_class->finalize = tb_sound_recorder_finalize;
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_STATE,
+                  g_param_spec_enum ("state",
+                                     "State",
+                                     "State of the sound recorder",
+                                     TB_TYPE_SOUND_RECORDER_STATE,
+                                     TB_SOUND_RECORDER_STATE_UNKNOWN,
+                                     G_PARAM_READABLE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_FILENAME,
+                  g_param_spec_string ("filename",
+                                       "Filename",
+                                       "Filename to save sound to",
+                                       NULL,
+                                       G_PARAM_READWRITE |
+                                       G_PARAM_CONSTRUCT));
+}
+
+static void
+tb_sound_recorder_init (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+
+  recorder->priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  recorder->priv->state = TB_SOUND_RECORDER_STATE_UNKNOWN;
+  recorder->priv->filename = NULL;
+  recorder->priv->pipeline = NULL;
+  recorder->priv->bus = NULL;
+}
+
+void
+tb_sound_recorder_start (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+
+  g_return_if_fail (TB_IS_SOUND_RECORDER (recorder));
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (tb_sound_recorder_ensure_pipeline (recorder))
+    gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+}
+
+void
+tb_sound_recorder_stop (TbSoundRecorder *recorder)
+{
+  TbSoundRecorderPrivate *priv;
+  GstElement *source;
+
+  g_return_if_fail (TB_IS_SOUND_RECORDER (recorder));
+
+  priv = TB_SOUND_RECORDER_GET_PRIVATE (recorder);
+
+  if (priv->pipeline == NULL)
+    return;
+
+  gst_element_send_event (priv->pipeline, gst_event_new_eos());
+}
diff --git a/src/tb/tb-sound-recorder.h b/src/tb/tb-sound-recorder.h
new file mode 100644
index 0000000..31199ca
--- /dev/null
+++ b/src/tb/tb-sound-recorder.h
@@ -0,0 +1,47 @@
+#ifndef __TB_SOUND_RECORDER_H__
+#define __TB_SOUND_RECORDER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TB_TYPE_SOUND_RECORDER            (tb_sound_recorder_get_type ())
+#define TB_SOUND_RECORDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TB_TYPE_SOUND_RECORDER, TbSoundRecorder))
+#define TB_IS_SOUND_RECORDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TB_TYPE_SOUND_RECORDER))
+#define TB_SOUND_RECORDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  TB_TYPE_SOUND_RECORDER, TbSoundRecorderClass))
+#define TB_IS_SOUND_RECORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  TB_TYPE_SOUND_RECORDER))
+#define TB_SOUND_RECORDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  TB_TYPE_SOUND_RECORDER, TbSoundRecorderClass))
+
+typedef struct _TbSoundRecorder          TbSoundRecorder;
+typedef struct _TbSoundRecorderPrivate   TbSoundRecorderPrivate;
+typedef struct _TbSoundRecorderClass     TbSoundRecorderClass;
+
+typedef enum
+{
+  TB_SOUND_RECORDER_STATE_UNKNOWN   = 0,
+  TB_SOUND_RECORDER_STATE_IDLE      = 1,
+  TB_SOUND_RECORDER_STATE_RECORDING = 2,
+  TB_SOUND_RECORDER_STATE_DONE      = 3,
+  TB_SOUND_RECORDER_STATE_ERROR     = 4
+} TbSoundRecorderState;
+
+struct _TbSoundRecorder
+{
+  GObject parent_instance;
+
+  TbSoundRecorderPrivate *priv;
+};
+
+struct _TbSoundRecorderClass
+{
+  GObjectClass parent_class;
+};
+
+GType    tb_sound_recorder_get_type     (void) G_GNUC_CONST;
+
+void     tb_sound_recorder_start        (TbSoundRecorder *recorder);
+void     tb_sound_recorder_stop         (TbSoundRecorder *recorder);
+
+G_END_DECLS
+
+#endif /* __TB_SOUND_RECORDER_H__ */



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