configure.ac: ext/Makefile.am
authorAndy Wingo <wingo@pobox.com>
Fri, 2 Feb 2007 13:28:05 +0000 (13:28 +0000)
committerAndy Wingo <wingo@pobox.com>
Fri, 2 Feb 2007 13:28:05 +0000 (13:28 +0000)
Original commit message from CVS:
2007-02-02  Andy Wingo  <wingo@pobox.com>

* configure.ac:
* ext/Makefile.am
* ext/sndfile/Makefile.am:
* ext/sndfile/gstsf.c:
* ext/sndfile/gstsf.h:
* ext/sndfile/gstsfsink.c:
* ext/sndfile/gstsfsink.h: Port sfsink to 0.10. Works in pull or
push mode with interleaved float or int data.

ChangeLog
configure.ac
ext/Makefile.am
ext/sndfile/Makefile.am
ext/sndfile/gstsf.c
ext/sndfile/gstsf.h
ext/sndfile/gstsfsink.c [new file with mode: 0644]
ext/sndfile/gstsfsink.h [new file with mode: 0644]

index 562d846b5597946501ccad0e09b2ced4e9bc6e69..4471f2ebb07bf2bafef8f7246e22ecb8147aac9f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2007-02-02  Andy Wingo  <wingo@pobox.com>
+
+       * configure.ac: 
+       * ext/Makefile.am
+       * ext/sndfile/Makefile.am:
+       * ext/sndfile/gstsf.c:
+       * ext/sndfile/gstsf.h:
+       * ext/sndfile/gstsfsink.c:
+       * ext/sndfile/gstsfsink.h: Port sfsink to 0.10. Works in pull or
+       push mode with interleaved float or int data.
+
 2007-02-02  Tim-Philipp Müller  <tim at centricular dot net>
 
        * ext/alsaspdif/alsaspdifsink.c: (plugin_init):
index ec10ae01c9d56a8e1c72f89def1825e20621062d..da5a78c04932d771bfe722d14f11592b67910bab 100644 (file)
@@ -750,6 +750,14 @@ GST_CHECK_FEATURE(SDL, [SDL plug-in], sdlvideosink sdlaudiosink, [
     AM_PATH_SDL(, HAVE_SDL=yes, HAVE_SDL=no)
 ])
 
+dnl *** sndfile ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_SNDFILE, true)
+GST_CHECK_FEATURE(SNDFILE, [sndfile plug-in], sfsrc sfsink, [
+  PKG_CHECK_MODULES(SNDFILE, sndfile >= 1.0.0, HAVE_SNDFILE="yes", HAVE_SNDFILE="no")
+  AC_SUBST(SNDFILE_CFLAGS)
+  AC_SUBST(SNDFILE_LIBS)
+])
+
 dnl *** soundtouch ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_SOUNDTOUCH, true)
 GST_CHECK_FEATURE(SOUNDTOUCH, [soundtouch plug-in], soundtouch, [
@@ -889,6 +897,7 @@ AM_CONDITIONAL(USE_MUSICBRAINZ, false)
 AM_CONDITIONAL(USE_MYTHTV, false)
 AM_CONDITIONAL(USE_NEON, false)
 AM_CONDITIONAL(USE_SDL, false)
+AM_CONDITIONAL(USE_SNDFILE, false)
 AM_CONDITIONAL(USE_SOUNDTOUCH, false)
 AM_CONDITIONAL(USE_SPC, false)
 AM_CONDITIONAL(USE_SWFDEC, false)
@@ -1005,6 +1014,7 @@ ext/musicbrainz/Makefile
 ext/mythtv/Makefile
 ext/neon/Makefile
 ext/sdl/Makefile
+ext/sndfile/Makefile
 ext/soundtouch/Makefile
 ext/spc/Makefile
 ext/swfdec/Makefile
index 75b5323b49ceca3af51a4d798ca7b1f816f467a4..4e960c8e2f70b036299ad715d8dad989b2d1592d 100644 (file)
@@ -202,11 +202,11 @@ SHOUT_DIR=
 SMOOTHWAVE_DIR=
 # endif
 
-if USE_SNDFILE
-SNDFILE_DIR=sndfile
-else
+if USE_SNDFILE
+SNDFILE_DIR=sndfile
+else
 SNDFILE_DIR=
-endif
+endif
 
 if USE_SOUNDTOUCH
 SOUNDTOUCH_DIR=soundtouch
@@ -316,6 +316,7 @@ DIST_SUBDIRS= \
        mythtv \
        neon \
        sdl \
+       sndfile \
        soundtouch \
        spc \
        swfdec \
index 23a86d717b24806d0aa1ebc5a532c5d80c5544d0..bd05115366026a69ea7cbdab04f6e1175a2f524c 100644 (file)
@@ -1,9 +1,9 @@
 
 plugin_LTLIBRARIES = libgstsndfile.la
 
-libgstsndfile_la_SOURCES = gstsf.c
-libgstsndfile_la_CFLAGS = $(GST_CFLAGS) $(SNDFILE_CFLAGS)
-libgstsndfile_la_LIBADD = $(SNDFILE_LIBS)
+libgstsndfile_la_SOURCES = gstsf.c gstsfsink.c
+libgstsndfile_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SNDFILE_CFLAGS)
+libgstsndfile_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(SNDFILE_LIBS)
 libgstsndfile_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
-noinst_HEADERS = gstsf.h
+noinst_HEADERS = gstsf.h gstsfsink.h
index e6313ed575b95430536b2801ff8fcea737caf5c5..084e69ac33bedad27542df9b04a1cf7dcd14dac9 100644 (file)
 #include "config.h"
 #endif
 
-#include "gst/gst-i18n-plugin.h"
 #include <string.h>
-#include <gst/gst.h>
-
-#include <gst/audio/audio.h>
 
 #include "gstsf.h"
 
 
-static const GstElementDetails sfsrc_details =
-GST_ELEMENT_DETAILS ("Sndfile source",
-    "Source/Audio",
-    "Read audio streams from disk using libsndfile",
-    "Andy Wingo <wingo at pobox dot com>");
-
-static const GstElementDetails sfsink_details =
-GST_ELEMENT_DETAILS ("Sndfile sink",
-    "Sink/Audio",
-    "Write audio streams to disk using libsndfile",
-    "Andy Wingo <wingo at pobox dot com>");
-
-enum
-{
-  ARG_0,
-  ARG_LOCATION,
-  ARG_MAJOR_TYPE,
-  ARG_MINOR_TYPE,
-  ARG_LOOP,
-  ARG_CREATE_PADS
-};
-
-static GstStaticPadTemplate sf_src_factory = GST_STATIC_PAD_TEMPLATE ("src%d",
-    GST_PAD_SRC,
-    GST_PAD_REQUEST,
-    GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
-    );
-
-static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink%d",
-    GST_PAD_SINK,
-    GST_PAD_REQUEST,
-    GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
-    );
-
-#define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type())
-static GType
+GType
 gst_sf_major_types_get_type (void)
 {
   static GType sf_major_types_type = 0;
@@ -94,11 +55,11 @@ gst_sf_major_types_get_type (void)
       if (k > 0
           && strcmp (sf_major_types[k].value_nick,
               sf_major_types[k - 1].value_nick) == 0) {
-        g_free (sf_major_types[k].value_nick);
+        g_free ((gchar *) sf_major_types[k].value_nick);
         sf_major_types[k].value_nick =
             g_strconcat (sf_major_types[k - 1].value_nick, "-",
             sf_major_types[k].value_name, NULL);
-        g_strcanon (sf_major_types[k].value_nick,
+        g_strcanon ((gchar *) sf_major_types[k].value_nick,
             G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
       }
     }
@@ -109,8 +70,7 @@ gst_sf_major_types_get_type (void)
   return sf_major_types_type;
 }
 
-#define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type())
-static GType
+GType
 gst_sf_minor_types_get_type (void)
 {
   static GType sf_minor_types_type = 0;
@@ -131,8 +91,8 @@ gst_sf_minor_types_get_type (void)
       sf_minor_types[k].value = format_info.format;
       sf_minor_types[k].value_name = g_strdup (format_info.name);
       sf_minor_types[k].value_nick = g_ascii_strdown (format_info.name, -1);
-      g_strcanon (sf_minor_types[k].value_nick, G_CSET_a_2_z G_CSET_DIGITS "-",
-          '-');
+      g_strcanon ((gchar *) sf_minor_types[k].value_nick,
+          G_CSET_a_2_z G_CSET_DIGITS "-", '-');
     }
 
     sf_minor_types_type =
@@ -141,731 +101,13 @@ gst_sf_minor_types_get_type (void)
   return sf_minor_types_type;
 }
 
-static void gst_sfsrc_base_init (gpointer g_class);
-static void gst_sfsink_base_init (gpointer g_class);
-static void gst_sf_class_init (GstSFClass * klass);
-static void gst_sf_init (GstSF * this);
-static void gst_sf_dispose (GObject * object);
-static void gst_sf_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec);
-static void gst_sf_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec);
-
-static GstClock *gst_sf_get_clock (GstElement * element);
-static void gst_sf_set_clock (GstElement * element, GstClock * clock);
-static GstPad *gst_sf_request_new_pad (GstElement * element,
-    GstPadTemplate * templ, const gchar * unused);
-static void gst_sf_release_request_pad (GstElement * element, GstPad * pad);
-static GstStateChangeReturn gst_sf_change_state (GstElement * element,
-    GstStateChange transition);
-
-static GstPadLinkReturn gst_sf_link (GstPad * pad, const GstCaps * caps);
-
-static void gst_sf_loop (GstElement * element);
-
-static GstClockTime gst_sf_get_time (GstClock * clock, gpointer data);
-
-static gboolean gst_sf_open_file (GstSF * this);
-static void gst_sf_close_file (GstSF * this);
-
-static GstElementClass *parent_class = NULL;
-
-GST_DEBUG_CATEGORY_STATIC (gstsf_debug);
-#define INFO(...) \
-    GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, NULL, __VA_ARGS__)
-#define INFO_OBJ(obj,...) \
-    GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, obj, __VA_ARGS__)
-
-GType
-gst_sf_get_type (void)
-{
-  static GType sf_type = 0;
-
-  if (!sf_type) {
-    static const GTypeInfo sf_info = {
-      sizeof (GstSFClass), NULL,
-      NULL,
-      (GClassInitFunc) NULL,    /* don't even initialize the class */
-      NULL,
-      NULL,
-      sizeof (GstSF),
-      0,
-      (GInstanceInitFunc) NULL  /* abstract base class */
-    };
-
-    sf_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSF", &sf_info, 0);
-  }
-  return sf_type;
-}
-
-GType
-gst_sfsrc_get_type (void)
-{
-  static GType sfsrc_type = 0;
-
-  if (!sfsrc_type) {
-    static const GTypeInfo sfsrc_info = {
-      sizeof (GstSFClass),
-      gst_sfsrc_base_init,
-      NULL,
-      (GClassInitFunc) gst_sf_class_init,
-      NULL,
-      NULL,
-      sizeof (GstSF),
-      0,
-      (GInstanceInitFunc) gst_sf_init,
-    };
-
-    sfsrc_type =
-        g_type_register_static (GST_TYPE_SF, "GstSFSrc", &sfsrc_info, 0);
-  }
-  return sfsrc_type;
-}
-
-GType
-gst_sfsink_get_type (void)
-{
-  static GType sfsink_type = 0;
-
-  if (!sfsink_type) {
-    static const GTypeInfo sfsink_info = {
-      sizeof (GstSFClass),
-      gst_sfsink_base_init,
-      NULL,
-      (GClassInitFunc) gst_sf_class_init,
-      NULL,
-      NULL,
-      sizeof (GstSF),
-      0,
-      (GInstanceInitFunc) gst_sf_init,
-    };
-
-    sfsink_type =
-        g_type_register_static (GST_TYPE_SF, "GstSFSink", &sfsink_info, 0);
-  }
-  return sfsink_type;
-}
-
-static void
-gst_sfsrc_base_init (gpointer g_class)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sf_src_factory));
-  gst_element_class_set_details (element_class, &sfsrc_details);
-}
-
-static void
-gst_sfsink_base_init (gpointer g_class)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sf_sink_factory));
-  gst_element_class_set_details (element_class, &sfsink_details);
-}
-
-static void
-gst_sf_class_init (GstSFClass * klass)
-{
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
-  GParamSpec *pspec;
-
-  gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  gst_element_class_install_std_props (gstelement_class, "location",
-      ARG_LOCATION, G_PARAM_READWRITE, NULL);
-  pspec = g_param_spec_enum
-      ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
-      SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
-  g_object_class_install_property (gobject_class, ARG_MAJOR_TYPE, pspec);
-  pspec = g_param_spec_enum
-      ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
-      SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
-  g_object_class_install_property (gobject_class, ARG_MINOR_TYPE, pspec);
-
-  if (G_TYPE_FROM_CLASS (klass) == GST_TYPE_SFSRC) {
-    pspec = g_param_spec_boolean ("loop", "Loop?", "Loop the output?",
-        FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
-    g_object_class_install_property (gobject_class, ARG_LOOP, pspec);
-    pspec =
-        g_param_spec_boolean ("create-pads", "Create pads?",
-        "Create one pad for each channel in the sound file?", TRUE,
-        G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
-    g_object_class_install_property (gobject_class, ARG_CREATE_PADS, pspec);
-  }
-
-  gobject_class->dispose = gst_sf_dispose;
-  gobject_class->set_property = gst_sf_set_property;
-  gobject_class->get_property = gst_sf_get_property;
-
-  gstelement_class->get_clock = gst_sf_get_clock;
-  gstelement_class->set_clock = gst_sf_set_clock;
-  gstelement_class->change_state = gst_sf_change_state;
-  gstelement_class->request_new_pad = gst_sf_request_new_pad;
-  gstelement_class->release_pad = gst_sf_release_request_pad;
-}
-
-static void
-gst_sf_init (GstSF * this)
-{
-  gst_element_set_loop_function (GST_ELEMENT (this), gst_sf_loop);
-  this->provided_clock = gst_audio_clock_new ("sfclock", gst_sf_get_time, this);
-  gst_object_set_parent (GST_OBJECT (this->provided_clock), GST_OBJECT (this));
-}
-
-static void
-gst_sf_dispose (GObject * object)
-{
-  GstSF *this = (GstSF *) object;
-
-  if (this->provided_clock) {
-    gst_object_unparent (GST_OBJECT (this->provided_clock));
-    this->provided_clock = NULL;
-  }
-
-  G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_sf_set_property (GObject * object, guint prop_id, const GValue * value,
-    GParamSpec * pspec)
-{
-  GstSF *this = GST_SF (object);
-
-  switch (prop_id) {
-    case ARG_LOCATION:
-      if (GST_OBJECT_FLAG_IS_SET (object, GST_SF_OPEN))
-        gst_sf_close_file (this);
-      if (this->filename)
-        g_free (this->filename);
-
-      if (g_value_get_string (value))
-        this->filename = g_strdup (g_value_get_string (value));
-      else
-        this->filename = NULL;
-
-      if (this->filename)
-        gst_sf_open_file (this);
-      break;
-
-    case ARG_MAJOR_TYPE:
-      this->format_major = g_value_get_enum (value);
-      break;
-
-    case ARG_MINOR_TYPE:
-      this->format_subtype = g_value_get_enum (value);
-      break;
-
-    case ARG_LOOP:
-      this->loop = g_value_get_boolean (value);
-      break;
-
-    case ARG_CREATE_PADS:
-      this->create_pads = g_value_get_boolean (value);
-      if (this->file && this->create_pads) {
-        int i;
-
-        for (i = g_list_length (this->channels); i < this->numchannels; i++)
-          gst_element_get_request_pad ((GstElement *) this, "src%d");
-      }
-      break;
-
-    default:
-      break;
-  }
-}
-
-static void
-gst_sf_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
-{
-  GstSF *this = GST_SF (object);
-
-  switch (prop_id) {
-    case ARG_LOCATION:
-      g_value_set_string (value, this->filename);
-      break;
-
-    case ARG_MAJOR_TYPE:
-      g_value_set_enum (value, this->format_major);
-      break;
-
-    case ARG_MINOR_TYPE:
-      g_value_set_enum (value, this->format_subtype);
-      break;
-
-    case ARG_LOOP:
-      g_value_set_boolean (value, this->loop);
-      break;
-
-    case ARG_CREATE_PADS:
-      g_value_set_boolean (value, this->create_pads);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static GstClock *
-gst_sf_get_clock (GstElement * element)
-{
-  GstSF *this = GST_SF (element);
-
-  return this->provided_clock;
-}
-
-static void
-gst_sf_set_clock (GstElement * element, GstClock * clock)
-{
-  GstSF *this = GST_SF (element);
-
-  this->clock = clock;
-}
-
-static GstClockTime
-gst_sf_get_time (GstClock * clock, gpointer data)
-{
-  GstSF *this = GST_SF (data);
-
-  return this->time;
-}
-
-static GstStateChangeReturn
-gst_sf_change_state (GstElement * element, GstStateChange transition)
-{
-  GstSF *this = GST_SF (element);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), TRUE);
-      break;
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock),
-          FALSE);
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      if (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
-        gst_sf_close_file (this);
-      break;
-  }
-
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
-  return GST_STATE_CHANGE_SUCCESS;
-}
-
-static GstPad *
-gst_sf_request_new_pad (GstElement * element, GstPadTemplate * templ,
-    const gchar * unused)
-{
-  gchar *name;
-  GstSF *this;
-  GstSFChannel *channel;
-
-  this = GST_SF (element);
-  channel = g_new0 (GstSFChannel, 1);
-
-  if (templ->direction == GST_PAD_SINK) {
-    /* we have an SFSink */
-    name = g_strdup_printf ("sink%d", this->channelcount);
-    this->numchannels++;
-    if (this->file) {
-      gst_sf_close_file (this);
-      gst_sf_open_file (this);
-    }
-  } else {
-    /* we have an SFSrc */
-    name = g_strdup_printf ("src%d", this->channelcount);
-  }
-
-  channel->pad = gst_pad_new_from_template (templ, name);
-  gst_element_add_pad (GST_ELEMENT (this), channel->pad);
-  gst_pad_set_link_function (channel->pad, gst_sf_link);
-
-  this->channels = g_list_append (this->channels, channel);
-  this->channelcount++;
-
-  INFO_OBJ (element, "added pad %s\n", name);
-
-  g_free (name);
-  return channel->pad;
-}
-
-static void
-gst_sf_release_request_pad (GstElement * element, GstPad * pad)
-{
-  GstSF *this;
-  GstSFChannel *channel = NULL;
-  GList *l;
-
-  this = GST_SF (element);
-
-  if (GST_STATE (element) == GST_STATE_PLAYING) {
-    g_warning
-        ("You can't release a request pad if the element is PLAYING, sorry.");
-    return;
-  }
-
-  for (l = this->channels; l; l = l->next) {
-    if (GST_SF_CHANNEL (l)->pad == pad) {
-      channel = GST_SF_CHANNEL (l);
-      break;
-    }
-  }
-
-  g_return_if_fail (channel != NULL);
-
-  INFO_OBJ (element, "Releasing request pad %s", GST_PAD_NAME (channel->pad));
-
-  if (GST_OBJECT_FLAG_IS_SET (element, GST_SF_OPEN))
-    gst_sf_close_file (this);
-
-  gst_element_remove_pad (element, channel->pad);
-  this->channels = g_list_remove (this->channels, channel);
-  this->numchannels--;
-  g_free (channel);
-}
-
-static GstPadLinkReturn
-gst_sf_link (GstPad * pad, const GstCaps * caps)
-{
-  GstSF *this = (GstSF *) GST_OBJECT_PARENT (pad);
-  GstStructure *structure;
-
-  structure = gst_caps_get_structure (caps, 0);
-
-  gst_structure_get_int (structure, "rate", &this->rate);
-  gst_structure_get_int (structure, "buffer-frames", &this->buffer_frames);
-
-  INFO_OBJ (this, "linked pad %s:%s with fixed caps, rate=%d, frames=%d",
-      GST_DEBUG_PAD_NAME (pad), this->rate, this->buffer_frames);
-
-  if (this->numchannels) {
-    /* we can go ahead and allocate our buffer */
-    if (this->buffer)
-      g_free (this->buffer);
-    this->buffer =
-        g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
-    memset (this->buffer, 0,
-        this->numchannels * this->buffer_frames * sizeof (float));
-  }
-  return GST_PAD_LINK_OK;
-}
-
-static gboolean
-gst_sf_open_file (GstSF * this)
-{
-  int mode;
-  SF_INFO info;
-
-  g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN), FALSE);
-
-  this->time = 0;
-
-  if (!this->filename) {
-    GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
-        (_("No filename specified.")), (NULL));
-    return FALSE;
-  }
-
-  if (GST_IS_SFSRC (this)) {
-    mode = SFM_READ;
-    info.format = 0;
-  } else {
-    if (!this->rate) {
-      INFO_OBJ (this, "Not opening %s yet because caps are not set",
-          this->filename);
-      return FALSE;
-    } else if (!this->numchannels) {
-      INFO_OBJ (this, "Not opening %s yet because we have no input channels",
-          this->filename);
-      return FALSE;
-    }
-
-    mode = SFM_WRITE;
-    this->format = this->format_major | this->format_subtype;
-    info.samplerate = this->rate;
-    info.channels = this->numchannels;
-    info.format = this->format;
-
-    INFO_OBJ (this, "Opening %s with rate %d, %d channels, format 0x%x",
-        this->filename, info.samplerate, info.channels, info.format);
-
-    if (!sf_format_check (&info)) {
-      GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
-          ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
-              info.samplerate, info.channels, info.format));
-      return FALSE;
-    }
-  }
-
-  this->file = sf_open (this->filename, mode, &info);
-
-  if (!this->file) {
-    GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
-        (_("Could not open file \"%s\" for writing."), this->filename),
-        ("soundfile error: %s", sf_strerror (NULL)));
-    return FALSE;
-  }
-
-  if (GST_IS_SFSRC (this)) {
-    GList *l = NULL;
-
-    /* the number of channels in the file can be different than the number of
-     * pads */
-    this->numchannels = info.channels;
-    this->rate = info.samplerate;
-
-    if (this->create_pads) {
-      int i;
-
-      for (i = g_list_length (this->channels); i < this->numchannels; i++)
-        gst_element_get_request_pad ((GstElement *) this, "src%d");
-    }
-
-    for (l = this->channels; l; l = l->next)
-      /* queue the need to set caps */
-      GST_SF_CHANNEL (l)->caps_set = FALSE;
-  }
-
-  GST_OBJECT_FLAG_SET (this, GST_SF_OPEN);
-
-  return TRUE;
-}
-
-static void
-gst_sf_close_file (GstSF * this)
-{
-  int err = 0;
-
-  g_return_if_fail (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN));
-
-  INFO_OBJ (this, "Closing file %s", this->filename);
-
-  if ((err = sf_close (this->file)))
-    GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
-        ("Could not close file file \"%s\".", this->filename),
-        ("soundfile error: %s", strerror (err)));
-  else
-    GST_OBJECT_FLAG_UNSET (this, GST_SF_OPEN);
-
-  this->file = NULL;
-  if (this->buffer)
-    g_free (this->buffer);
-  this->buffer = NULL;
-}
-
-static void
-gst_sf_loop (GstElement * element)
-{
-  GstSF *this;
-  GList *l = NULL;
-
-  this = (GstSF *) element;
-
-  if (this->channels == NULL) {
-    GST_ELEMENT_ERROR (element, CORE, PAD, (NULL),
-        ("You must connect at least one pad to sndfile elements."));
-    return;
-  }
-
-  if (GST_IS_SFSRC (this)) {
-    sf_count_t read;
-    gint i, j;
-    int eos = 0;
-    int buffer_frames = this->buffer_frames;
-    int nchannels = this->numchannels;
-    GstSFChannel *channel = NULL;
-    gfloat *data;
-    gfloat *buf = this->buffer;
-    GstBuffer *out;
-
-    if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
-      if (!gst_sf_open_file (this))
-        return;                 /* we've already set gst_element_error */
-
-    if (buffer_frames == 0) {
-      /* we have to set the caps later */
-      buffer_frames = this->buffer_frames = 1024;
-    }
-    if (buf == NULL) {
-      buf = this->buffer =
-          g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
-      memset (this->buffer, 0,
-          this->numchannels * this->buffer_frames * sizeof (float));
-    }
-
-    read = sf_readf_float (this->file, buf, buffer_frames);
-    if (read < buffer_frames)
-      eos = 1;
-
-    if (read)
-      for (i = 0, l = this->channels; l; l = l->next, i++) {
-        channel = GST_SF_CHANNEL (l);
-
-        /* don't push on disconnected pads -- useful for ::create-pads=TRUE */
-        if (!GST_PAD_PEER (channel->pad))
-          continue;
-
-        if (!channel->caps_set) {
-          GstCaps *caps =
-              gst_caps_copy (GST_PAD_CAPS (GST_SF_CHANNEL (l)->pad));
-          if (!caps)
-            caps = gst_caps_copy
-                (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (GST_SF_CHANNEL
-                        (l)->pad)));
-          gst_caps_set_simple (caps, "rate", G_TYPE_INT, this->rate,
-              "buffer-frames", G_TYPE_INT, this->buffer_frames, NULL);
-          if (!gst_pad_try_set_caps (GST_SF_CHANNEL (l)->pad, caps)) {
-            GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, (NULL),
-                ("Opened file with sample rate %d, but could not set caps",
-                    this->rate));
-            gst_sf_close_file (this);
-            return;
-          }
-          channel->caps_set = TRUE;
-        }
-
-        out = gst_buffer_new_and_alloc (read * sizeof (float));
-        data = (gfloat *) GST_BUFFER_DATA (out);
-        for (j = 0; j < read; j++)
-          data[j] = buf[j * nchannels + i % nchannels];
-        gst_pad_push (channel->pad, GST_DATA (out));
-      }
-
-    this->time += read * (GST_SECOND / this->rate);
-    gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock,
-        this->time);
-
-    if (eos) {
-      if (this->loop) {
-        sf_seek (this->file, (sf_count_t) 0, SEEK_SET);
-        eos = 0;
-      } else {
-        for (l = this->channels; l; l = l->next)
-          gst_pad_push (GST_SF_CHANNEL (l)->pad,
-              GST_DATA (gst_event_new (GST_EVENT_EOS)));
-        gst_element_set_eos (element);
-      }
-    }
-  } else {
-    sf_count_t written, num_to_write;
-    gint i, j;
-    int buffer_frames = this->buffer_frames;
-    int nchannels = this->numchannels;
-    GstSFChannel *channel = NULL;
-    gfloat *data;
-    gfloat *buf = this->buffer;
-    GstBuffer *in;
-
-    /* the problem: we can't allocate a buffer for pulled data before caps is
-     * set, and we can't open the file without the sample rate from the
-     * caps... */
-
-    num_to_write = buffer_frames;
-
-    INFO_OBJ (this, "looping, buffer_frames=%d, nchannels=%d", buffer_frames,
-        nchannels);
-
-    for (i = 0, l = this->channels; l; l = l->next, i++) {
-      channel = GST_SF_CHANNEL (l);
-
-    pull_again:
-      in = GST_BUFFER (gst_pad_pull (channel->pad));
-
-      if (buffer_frames == 0) {
-        /* pulling a buffer from the pad should have caused capsnego to occur,
-           which then would set this->buffer_frames to a new value */
-        buffer_frames = this->buffer_frames;
-        if (buffer_frames == 0) {
-          GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
-              ("format wasn't negotiated before chain function"));
-          return;
-        }
-        buf = this->buffer;
-        num_to_write = buffer_frames;
-      }
-
-      if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
-        if (!gst_sf_open_file (this))
-          return;               /* we've already set gst_element_error */
-
-      if (GST_IS_EVENT (in)) {
-        switch (GST_EVENT_TYPE (in)) {
-          case GST_EVENT_EOS:
-          case GST_EVENT_INTERRUPT:
-            num_to_write = 0;
-            break;
-          default:
-            goto pull_again;
-            break;
-        }
-      }
-
-      if (num_to_write) {
-        data = (gfloat *) GST_BUFFER_DATA (in);
-        num_to_write =
-            MIN (num_to_write, GST_BUFFER_SIZE (in) / sizeof (gfloat));
-        for (j = 0; j < num_to_write; j++)
-          buf[j * nchannels + i % nchannels] = data[j];
-      }
-
-      gst_data_unref ((GstData *) in);
-    }
-
-    if (num_to_write) {
-      written = sf_writef_float (this->file, buf, num_to_write);
-      if (written != num_to_write)
-        GST_ELEMENT_ERROR (element, RESOURCE, WRITE,
-            (_("Could not write to file \"%s\"."), this->filename),
-            ("soundfile error: %s", sf_strerror (this->file)));
-    }
-
-    this->time += num_to_write * (GST_SECOND / this->rate);
-    gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock,
-        this->time);
-
-    if (num_to_write != buffer_frames)
-      gst_element_set_eos (element);
-  }
-}
-
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  if (!gst_library_load ("gstaudio"))
+  if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE,
+          gst_sf_sink_get_type ()))
     return FALSE;
 
-  GST_DEBUG_CATEGORY_INIT (gstsf_debug, "sf",
-      GST_DEBUG_FG_WHITE | GST_DEBUG_BG_GREEN | GST_DEBUG_BOLD,
-      "libsndfile plugin");
-
-  if (!gst_element_register (plugin, "sfsrc", GST_RANK_NONE, GST_TYPE_SFSRC))
-    return FALSE;
-
-  if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, GST_TYPE_SFSINK))
-    return FALSE;
-
-#ifdef ENABLE_NLS
-  setlocale (LC_ALL, "");
-  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
-#endif /* ENABLE_NLS */
-
   return TRUE;
 }
 
index 8af46a7ebccb1b1e406c5b77735c74d84ec5654a..129ddd93b5d28b237ae24d327e2fdd374b4ef649 100644 (file)
 G_BEGIN_DECLS
 
 
-#define GST_TYPE_SF \
-  (gst_sf_get_type())
-#define GST_SF(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SF,GstSF))
-#define GST_SF_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SF,GstSFClass))
-#define GST_IS_SF(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SF))
-#define GST_IS_SF_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SF))
+#define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type())
+#define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type())
 
-#define GST_TYPE_SFSRC \
-  (gst_sfsrc_get_type())
-#define GST_SFSRC(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SFSRC,GstSF))
-#define GST_SFSRC_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SFSRC,GstSFClass))
-#define GST_IS_SFSRC(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SFSRC))
-#define GST_IS_SFSRC_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SFSRC))
+GType gst_sf_major_types_get_type (void);
+GType gst_sf_minor_types_get_type (void);
 
-#define GST_TYPE_SFSINK \
-  (gst_sfsink_get_type())
-#define GST_SFSINK(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SFSINK,GstSF))
-#define GST_SFSINK_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SFSINK,GstSFClass))
-#define GST_IS_SFSINK(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SFSINK))
-#define GST_IS_SFSINK_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SFSINK))
-
-typedef struct _GstSF GstSF;
-typedef struct _GstSFClass GstSFClass;
-
-typedef enum {
-  GST_SF_OPEN           = (GST_ELEMENT_FLAG_LAST << 0),
-  GST_SF_FLAG_LAST      = (GST_ELEMENT_FLAG_LAST << 2),
-} GstSFlags;
-
-typedef struct {
-  GstPad *pad;
-  gint num;
-  gboolean caps_set;
-} GstSFChannel;
-
-#define GST_SF_CHANNEL(l) ((GstSFChannel*)l->data)
-
-struct _GstSF {
-  GstElement element;
-  GList *channels;
-
-  GstClock *clock, *provided_clock;
-
-  gchar *filename;
-  SNDFILE *file;
-  void *buffer;
-
-  gboolean loop;
-  gboolean create_pads;
-  gint channelcount;
-  gint numchannels;
-  gint format_major;
-  gint format_subtype;
-  gint format;
-
-  gint rate;
-  gint buffer_frames;
-
-  guint64 time;
-};
-
-struct _GstSFClass {
-  GstElementClass parent_class;
-};
-
-GType   gst_sf_get_type         (void);
-GType   gst_sfsrc_get_type      (void);
-GType   gst_sfsink_get_type     (void);
+GType gst_sf_sink_get_type (void);
 
 
 G_END_DECLS
diff --git a/ext/sndfile/gstsfsink.c b/ext/sndfile/gstsfsink.c
new file mode 100644 (file)
index 0000000..4039a79
--- /dev/null
@@ -0,0 +1,528 @@
+/* GStreamer libsndfile plugin
+ * Copyright (C) 2007 Andy Wingo <wingo at pobox dot com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/audio/audio.h>
+
+#include <gst/gst-i18n-plugin.h>
+
+#include "gstsfsink.h"
+
+
+static const GstElementDetails sfsink_details =
+GST_ELEMENT_DETAILS ("Sndfile sink",
+    "Sink/Audio",
+    "Write audio streams to disk using libsndfile",
+    "Andy Wingo <wingo at pobox dot com>");
+
+enum
+{
+  PROP_0,
+  PROP_LOCATION,
+  PROP_MAJOR_TYPE,
+  PROP_MINOR_TYPE,
+  PROP_BUFFER_FRAMES
+};
+
+#define DEFAULT_BUFFER_FRAMES (256)
+
+static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-float, "
+        "rate = (int) [ 1, MAX ], "
+        "channels = (int) [ 1, MAX ], "
+        "endianness = (int) BYTE_ORDER, "
+        "width = (int) 32; "
+        "audio/x-raw-int, "
+        "rate = (int) [ 1, MAX ], "
+        "channels = (int) [ 1, MAX ], "
+        "endianness = (int) BYTE_ORDER, "
+        "width = (int) {16, 32}, "
+        "depth = (int) {16, 32}, " "signed = (boolean) true")
+    );
+
+GST_BOILERPLATE (GstSFSink, gst_sf_sink, GstBaseSink, GST_TYPE_BASE_SINK);
+
+static void gst_sf_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_sf_sink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static gboolean gst_sf_sink_start (GstBaseSink * bsink);
+static gboolean gst_sf_sink_stop (GstBaseSink * bsink);
+static void gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
+static gboolean gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
+static gboolean gst_sf_sink_activate_pull (GstBaseSink * bsink,
+    gboolean active);
+static GstFlowReturn gst_sf_sink_render (GstBaseSink * bsink,
+    GstBuffer * buffer);
+static gboolean gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event);
+
+static gboolean gst_sf_sink_open_file (GstSFSink * this);
+static void gst_sf_sink_close_file (GstSFSink * this);
+
+GST_DEBUG_CATEGORY_STATIC (gst_sf_debug);
+#define GST_CAT_DEFAULT gst_sf_debug
+
+static void
+gst_sf_sink_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  GST_DEBUG_CATEGORY_INIT (gst_sf_debug, "sfsink", 0, "sfsink element");
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sf_sink_factory));
+  gst_element_class_set_details (element_class, &sfsink_details);
+}
+
+static void
+gst_sf_sink_class_init (GstSFSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstBaseSinkClass *basesink_class;
+  GParamSpec *pspec;
+
+  gobject_class = (GObjectClass *) klass;
+  basesink_class = (GstBaseSinkClass *) klass;
+
+  gobject_class->set_property = gst_sf_sink_set_property;
+  gobject_class->get_property = gst_sf_sink_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_LOCATION,
+      g_param_spec_string ("location", "File Location",
+          "Location of the file to write", NULL, G_PARAM_READWRITE));
+  pspec = g_param_spec_enum
+      ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
+      SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+  g_object_class_install_property (gobject_class, PROP_MAJOR_TYPE, pspec);
+  pspec = g_param_spec_enum
+      ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
+      SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+  g_object_class_install_property (gobject_class, PROP_MINOR_TYPE, pspec);
+  pspec = g_param_spec_int
+      ("buffer-frames", "Buffer frames",
+      "Number of frames per buffer, in pull mode", 1, G_MAXINT,
+      DEFAULT_BUFFER_FRAMES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+  g_object_class_install_property (gobject_class, PROP_BUFFER_FRAMES, pspec);
+
+  basesink_class->get_times = NULL;
+  basesink_class->start = GST_DEBUG_FUNCPTR (gst_sf_sink_start);
+  basesink_class->stop = GST_DEBUG_FUNCPTR (gst_sf_sink_stop);
+  basesink_class->fixate = GST_DEBUG_FUNCPTR (gst_sf_sink_fixate);
+  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_sf_sink_set_caps);
+  basesink_class->activate_pull = GST_DEBUG_FUNCPTR (gst_sf_sink_activate_pull);
+  basesink_class->render = GST_DEBUG_FUNCPTR (gst_sf_sink_render);
+  basesink_class->event = GST_DEBUG_FUNCPTR (gst_sf_sink_event);
+}
+
+static void
+gst_sf_sink_init (GstSFSink * this, GstSFSinkClass * klass)
+{
+  GST_BASE_SINK (this)->can_activate_pull = TRUE;
+}
+
+static void
+gst_sf_sink_set_location (GstSFSink * this, const gchar * location)
+{
+  if (this->file)
+    goto was_open;
+
+  if (this->location)
+    g_free (this->location);
+
+  this->location = location ? g_strdup (location) : NULL;
+
+  return;
+
+was_open:
+  {
+    g_warning ("Changing the `location' property on sfsink when "
+        "a file is open not supported.");
+    return;
+  }
+}
+
+
+static void
+gst_sf_sink_set_property (GObject * object, guint prop_id, const GValue * value,
+    GParamSpec * pspec)
+{
+  GstSFSink *this = GST_SF_SINK (object);
+
+  switch (prop_id) {
+    case PROP_LOCATION:
+      gst_sf_sink_set_location (this, g_value_get_string (value));
+      break;
+
+    case PROP_MAJOR_TYPE:
+      this->format_major = g_value_get_enum (value);
+      break;
+
+    case PROP_MINOR_TYPE:
+      this->format_subtype = g_value_get_enum (value);
+      break;
+
+    case PROP_BUFFER_FRAMES:
+      this->buffer_frames = g_value_get_int (value);
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void
+gst_sf_sink_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstSFSink *this = GST_SF_SINK (object);
+
+  switch (prop_id) {
+    case PROP_LOCATION:
+      g_value_set_string (value, this->location);
+      break;
+
+    case PROP_MAJOR_TYPE:
+      g_value_set_enum (value, this->format_major);
+      break;
+
+    case PROP_MINOR_TYPE:
+      g_value_set_enum (value, this->format_subtype);
+      break;
+
+    case PROP_BUFFER_FRAMES:
+      g_value_set_int (value, this->buffer_frames);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_sf_sink_start (GstBaseSink * bsink)
+{
+  /* pass */
+  return TRUE;
+}
+
+static gboolean
+gst_sf_sink_stop (GstBaseSink * bsink)
+{
+  GstSFSink *this = GST_SF_SINK (bsink);
+
+  if (this->file)
+    gst_sf_sink_close_file (this);
+
+  return TRUE;
+}
+
+static gboolean
+gst_sf_sink_open_file (GstSFSink * this)
+{
+  int mode;
+  SF_INFO info;
+
+  g_return_val_if_fail (this->file == NULL, FALSE);
+  g_return_val_if_fail (this->rate > 0, FALSE);
+  g_return_val_if_fail (this->channels > 0, FALSE);
+
+  if (!this->location)
+    goto no_filename;
+
+  mode = SFM_WRITE;
+  this->format = this->format_major | this->format_subtype;
+  info.samplerate = this->rate;
+  info.channels = this->channels;
+  info.format = this->format;
+
+  GST_INFO_OBJECT (this, "Opening %s with rate %d, %d channels, format 0x%x",
+      this->location, info.samplerate, info.channels, info.format);
+
+  if (!sf_format_check (&info))
+    goto bad_format;
+
+  this->file = sf_open (this->location, mode, &info);
+
+  if (!this->file)
+    goto open_failed;
+
+  return TRUE;
+
+no_filename:
+  {
+    GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
+        (_("No file name specified for writing.")), (NULL));
+    return FALSE;
+  }
+bad_format:
+  {
+    GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
+        ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
+            info.samplerate, info.channels, info.format));
+    return FALSE;
+  }
+open_failed:
+  {
+    GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
+        (_("Could not open file \"%s\" for writing."), this->location),
+        ("soundfile error: %s", sf_strerror (NULL)));
+    return FALSE;
+  }
+}
+
+static void
+gst_sf_sink_close_file (GstSFSink * this)
+{
+  int err = 0;
+
+  g_return_if_fail (this->file != NULL);
+
+  GST_INFO_OBJECT (this, "Closing file %s", this->location);
+
+  if ((err = sf_close (this->file)))
+    goto close_failed;
+
+  this->file = NULL;
+
+  return;
+
+close_failed:
+  {
+    GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
+        ("Could not close file file \"%s\".", this->location),
+        ("soundfile error: %s", sf_error_number (err)));
+    return;
+  }
+}
+
+static void
+gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
+{
+  GstStructure *s;
+  gint width, depth;
+
+  s = gst_caps_get_structure (caps, 0);
+
+  /* fields for all formats */
+  gst_structure_fixate_field_nearest_int (s, "rate", 44100);
+  gst_structure_fixate_field_nearest_int (s, "channels", 2);
+  gst_structure_fixate_field_nearest_int (s, "width", 16);
+
+  /* fields for int */
+  if (gst_structure_has_field (s, "depth")) {
+    gst_structure_get_int (s, "width", &width);
+    /* round width to nearest multiple of 8 for the depth */
+    depth = GST_ROUND_UP_8 (width);
+    gst_structure_fixate_field_nearest_int (s, "depth", depth);
+  }
+  if (gst_structure_has_field (s, "signed"))
+    gst_structure_fixate_field_boolean (s, "signed", TRUE);
+  if (gst_structure_has_field (s, "endianness"))
+    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
+}
+
+static gboolean
+gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+  GstSFSink *this = (GstSFSink *) bsink;
+  GstStructure *structure;
+  gint width, channels, rate;
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  if (!gst_structure_get_int (structure, "width", &width)
+      || !gst_structure_get_int (structure, "channels", &channels)
+      || !gst_structure_get_int (structure, "rate", &rate))
+    goto impossible;
+
+  if (gst_structure_has_name (structure, "audio/x-raw-int")) {
+    switch (width) {
+      case 16:
+        this->writer = (GstSFWriter) sf_writef_short;
+        break;
+      case 32:
+        this->writer = (GstSFWriter) sf_writef_int;
+        break;
+      default:
+        goto impossible;
+    }
+  } else {
+    switch (width) {
+      case 32:
+        this->writer = (GstSFWriter) sf_writef_float;
+        break;
+      default:
+        goto impossible;
+    }
+  }
+
+  this->bytes_per_frame = width * channels / 8;
+  this->rate = rate;
+  this->channels = channels;
+
+  return gst_sf_sink_open_file (this);
+
+impossible:
+  {
+    g_warning ("something impossible happened");
+    return FALSE;
+  }
+}
+
+/* with STREAM_LOCK
+ */
+static void
+gst_sf_sink_loop (GstPad * pad)
+{
+  GstSFSink *this;
+  GstBaseSink *basesink;
+  GstBuffer *buf = NULL;
+  GstFlowReturn result;
+
+  this = GST_SF_SINK (gst_pad_get_parent (pad));
+  basesink = GST_BASE_SINK (this);
+
+  result = gst_pad_pull_range (pad, basesink->offset,
+      this->buffer_frames * this->bytes_per_frame, &buf);
+  if (G_UNLIKELY (result != GST_FLOW_OK))
+    goto paused;
+
+  if (G_UNLIKELY (buf == NULL))
+    goto no_buffer;
+
+  basesink->offset += GST_BUFFER_SIZE (buf);
+
+  GST_PAD_PREROLL_LOCK (pad);
+  result = gst_sf_sink_render (basesink, buf);
+  GST_PAD_PREROLL_UNLOCK (pad);
+  if (G_UNLIKELY (result != GST_FLOW_OK))
+    goto paused;
+
+  gst_object_unref (this);
+
+  return;
+
+  /* ERRORS */
+paused:
+  {
+    GST_INFO_OBJECT (basesink, "pausing task, reason %s",
+        gst_flow_get_name (result));
+    gst_pad_pause_task (pad);
+    /* fatal errors and NOT_LINKED cause EOS */
+    if (GST_FLOW_IS_FATAL (result) || result == GST_FLOW_NOT_LINKED) {
+      gst_pad_send_event (pad, gst_event_new_eos ());
+      /* EOS does not cause an ERROR message */
+      if (result != GST_FLOW_UNEXPECTED) {
+        GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
+            (_("Internal data stream error.")),
+            ("stream stopped, reason %s", gst_flow_get_name (result)));
+      }
+    }
+    gst_object_unref (this);
+    return;
+  }
+no_buffer:
+  {
+    GST_INFO_OBJECT (this, "no buffer, pausing");
+    result = GST_FLOW_ERROR;
+    goto paused;
+  }
+}
+
+static gboolean
+gst_sf_sink_activate_pull (GstBaseSink * basesink, gboolean active)
+{
+  gboolean result;
+
+  if (active) {
+    /* start task */
+    result = gst_pad_start_task (basesink->sinkpad,
+        (GstTaskFunction) gst_sf_sink_loop, basesink->sinkpad);
+  } else {
+    /* step 2, make sure streaming finishes */
+    result = gst_pad_stop_task (basesink->sinkpad);
+  }
+
+  return result;
+}
+
+static GstFlowReturn
+gst_sf_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
+{
+  GstSFSink *this;
+  sf_count_t written, num_to_write;
+
+  this = (GstSFSink *) bsink;
+
+  if (GST_BUFFER_SIZE (buffer) % this->bytes_per_frame)
+    goto bad_length;
+
+  num_to_write = GST_BUFFER_SIZE (buffer) / this->bytes_per_frame;
+
+  written = this->writer (this->file, GST_BUFFER_DATA (buffer), num_to_write);
+  if (written != num_to_write)
+    goto short_write;
+
+  return GST_FLOW_OK;
+
+bad_length:
+  {
+    GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
+        (_("Could not write to file \"%s\"."), this->location),
+        ("bad buffer size: %u %% %d != 0", GST_BUFFER_SIZE (buffer),
+            this->bytes_per_frame));
+    return GST_FLOW_ERROR;
+  }
+short_write:
+  {
+    GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
+        (_("Could not write to file \"%s\"."), this->location),
+        ("soundfile error: %s", sf_strerror (this->file)));
+    return GST_FLOW_ERROR;
+  }
+}
+
+static gboolean
+gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event)
+{
+  GstSFSink *this;
+  GstEventType type;
+
+  this = (GstSFSink *) bsink;
+
+  type = GST_EVENT_TYPE (event);
+
+  switch (type) {
+    case GST_EVENT_EOS:
+      if (this->file)
+        sf_write_sync (this->file);
+      break;
+    default:
+      break;
+  }
+
+  return TRUE;
+}
diff --git a/ext/sndfile/gstsfsink.h b/ext/sndfile/gstsfsink.h
new file mode 100644 (file)
index 0000000..bf6c695
--- /dev/null
@@ -0,0 +1,73 @@
+/* GStreamer libsndfile plugin
+ * Copyright (C) 2003,2007 Andy Wingo <wingo at pobox dot com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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 __GST_SF_SINK_H__
+#define __GST_SF_SINK_H__
+
+
+#include "gstsf.h"
+#include <gst/base/gstbasesink.h>
+
+
+G_BEGIN_DECLS
+
+
+#define GST_TYPE_SF_SINK \
+  (gst_sf_sink_get_type())
+#define GST_SF_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SF_SINK,GstSFSink))
+#define GST_SF_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SF_SINK,GstSFSinkClass))
+#define GST_IS_SF_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SF_SINK))
+#define GST_IS_SF_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SF_SINK))
+
+typedef struct _GstSFSink GstSFSink;
+typedef struct _GstSFSinkClass GstSFSinkClass;
+
+typedef sf_count_t (*GstSFWriter)(SNDFILE *f, void *data, sf_count_t nframes);
+
+struct _GstSFSink {
+  GstBaseSink parent;
+
+  gchar *location;
+  SNDFILE *file;
+  GstSFWriter writer;
+  gint bytes_per_frame;
+
+  gint channels;
+  gint rate;
+
+  gint format_major;
+  gint format_subtype;
+  gint format;
+  gint buffer_frames;
+};
+
+struct _GstSFSinkClass {
+  GstBaseSinkClass parent_class;
+};
+
+
+G_END_DECLS
+
+
+#endif /* __GST_SF_SINK_H__ */