[the-board] [tb] Add initial code for TbSoundPlayer and TbSoundRecorder
- From: Lucas Rocha <lucasr src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [the-board] [tb] Add initial code for TbSoundPlayer and TbSoundRecorder
- Date: Thu, 6 Jan 2011 12:25:31 +0000 (UTC)
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]