+ removed the access_addr crap from GstAlsaPad ... just use this->access_addr[channel...
authorLeif Johnson <leif@ambient.2y.net>
Mon, 27 Jan 2003 12:59:24 +0000 (12:59 +0000)
committerLeif Johnson <leif@ambient.2y.net>
Mon, 27 Jan 2003 12:59:24 +0000 (12:59 +0000)
Original commit message from CVS:
+ removed the access_addr crap from GstAlsaPad ... just use
this->access_addr[channel] instead
+ completely reorganized and reindented code
+ removed the gst_alsa_sink_silence_on_channel function, needs to be completely
redone anyway
+ got alsasink to work on my machine finally ! yay !

ext/alsa/gstalsa.c
ext/alsa/gstalsa.h

index 780d5cf60ec585939a8c63d173f25aa55793144c..40982c178eb0a94bfb3dc62022c56f3c9745d789 100644 (file)
@@ -1,23 +1,22 @@
-/* -*- c-basic-offset: 4 -*- */
 /*
   Copyright (C) 2001 CodeFactory AB
   Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
   Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
-
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU 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
   General Public License for more details.
-
   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
* Copyright (C) 2001 CodeFactory AB
* Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
* Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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
* General Public License for more details.
+ *
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 
 #include <stdlib.h>
 #include <sys/time.h>
 
 /* elementfactory information */
 static GstElementDetails gst_alsa_sink_details = {
-    "Alsa Sink",
-    "Sink/Audio",
-    "LGPL",
-    "Output to a sound card via ALSA",
-    VERSION,
-    "Thomas Nyberg <thomas@codefactory.se>, "
-    "Andy Wingo <apwingo@eos.ncsu.edu>",
-    "(C) 2001 "
+  "Alsa Sink",
+  "Sink/Audio",
+  "LGPL",
+  "Output to a sound card via ALSA",
+  VERSION,
+  "Thomas Nyberg <thomas@codefactory.se>, "
+  "Andy Wingo <apwingo@eos.ncsu.edu>",
+  "(C) 2001 "
 };
 
 /* elementfactory information */
 static GstElementDetails gst_alsa_src_details = {
-    "Alsa Src",
-    "Source/Audio",
-    "LGPL",
-    "Read from a sound card via ALSA",
-    VERSION,
-    "Thomas Nyberg <thomas@codefactory.se>, "
-    "Andy Wingo <apwingo@eos.ncsu.edu>",
-    "(C) 2001"
+  "Alsa Src",
+  "Source/Audio",
+  "LGPL",
+  "Read from a sound card via ALSA",
+  VERSION,
+  "Thomas Nyberg <thomas@codefactory.se>, "
+  "Andy Wingo <apwingo@eos.ncsu.edu>",
+  "(C) 2001"
 };
 
-static GstElement *parent_class = NULL;
+/* GObject functions */
+static void gst_alsa_class_init (GstAlsaClass *klass);
+static void gst_alsa_init (GstAlsa *this);
+static void gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gst_alsa_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 
-static void gst_alsa_init(GstAlsa *this);
-static void gst_alsa_class_init(GstAlsaClass *klass);
+/* GStreamer functions for pads and state changing */
+static GstPadTemplate   *gst_alsa_src_pad_factory ();
+static GstPadTemplate   *gst_alsa_src_request_pad_factory ();
+static GstPadTemplate   *gst_alsa_sink_pad_factory ();
+static GstPadTemplate   *gst_alsa_sink_request_pad_factory ();
 
-static GstPadTemplate *gst_alsa_src_pad_factory();
-static GstPadTemplate *gst_alsa_src_request_pad_factory();
-static GstPadTemplate *gst_alsa_sink_pad_factory();
-static GstPadTemplate *gst_alsa_sink_request_pad_factory();
+static GstPad           *gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
+static GstPadLinkReturn  gst_alsa_link (GstPad *pad, GstCaps *caps);
+static GstCaps          *gst_alsa_caps (GstAlsa *this);
 
-static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
+static GstElementStateReturn gst_alsa_change_state (GstElement *element);
 
-static void gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static void gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
-static GstElementStateReturn gst_alsa_change_state(GstElement *element);
-static GstPadLinkReturn gst_alsa_link(GstPad *pad, GstCaps *caps);
+/* audio processing functions */
+static void     gst_alsa_loop (GstElement *element);
+static void     gst_alsa_xrun_recovery (GstAlsa *this);
 
-static GstCaps* gst_alsa_caps (GstAlsa *this);
-
-static gboolean gst_alsa_open_audio(GstAlsa *this);
-static gboolean gst_alsa_start_audio(GstAlsa *this);
-static void gst_alsa_stop_audio(GstAlsa *this);
-static void gst_alsa_close_audio(GstAlsa *this);
-
-static gboolean gst_alsa_set_params(GstAlsa *this);
-static void gst_alsa_loop (GstElement *element);
-static void gst_alsa_xrun_recovery (GstAlsa *this);
-static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames);
 static gboolean gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames);
+static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames);
+static void     gst_alsa_sink_check_event (GstAlsa *this, GstAlsaPad *pad);
 
-static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
-static void gst_alsa_release_channel_addresses (GstAlsa *this);
+/* alsa setup / start / stop functions */
+static gboolean gst_alsa_set_params (GstAlsa *this);
 
-static void gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes);
+static gboolean gst_alsa_open_audio (GstAlsa *this);
+static gboolean gst_alsa_start_audio (GstAlsa *this);
+static void     gst_alsa_stop_audio (GstAlsa *this);
+static void     gst_alsa_close_audio (GstAlsa *this);
 
-static void memset_interleave (char *dst, char val, unsigned int bytes, unsigned int unit_bytes, unsigned int skip_bytes);
+static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
+static void     gst_alsa_release_channel_addresses (GstAlsa *this);
 
 /* #define _DEBUG */
 #ifdef _DEBUG
@@ -91,1418 +90,1307 @@ static void memset_interleave (char *dst, char val, unsigned int bytes, unsigned
 #define DEBUG(text, args...)
 #endif
 
-
-enum {
-    ARG_0,
-    ARG_DEVICE,
-    ARG_FORMAT,
-    ARG_CHANNELS,
-    ARG_RATE,
-    ARG_PERIODCOUNT,
-    ARG_PERIODFRAMES,
-    ARG_DEBUG
-};
+/*** TYPE FUNCTIONS ***********************************************************/
 
 #define GST_TYPE_ALSA_FORMAT (gst_alsa_format_get_type())
 static GType
 gst_alsa_format_get_type (void)
 {
-    static GType type = 0;
-    static GEnumValue *values = NULL;
-    gint i, len = SND_PCM_FORMAT_GSM + 3;
-
-    if (values == NULL) {
-        /* the three: for -1, 0, and the terminating NULL */
-        values = g_new0 (GEnumValue, len);
-
-        for (i=0; i<len-1; i++) {
-            values[i].value = i-1; /* UNKNOWN is -1 */
-            values[i].value_name = g_strdup_printf ("%d", i-1);
-            values[i].value_nick = g_strdup (snd_pcm_format_name ((snd_pcm_format_t)i-1));
-        }
+  static GType type = 0;
+  static GEnumValue *values = NULL;
+  gint i, len = SND_PCM_FORMAT_GSM + 3;
+
+  if (values == NULL) {
+    /* the three: for -1, 0, and the terminating NULL */
+    values = g_new0 (GEnumValue, len);
+
+    for (i = 0; i < len - 1; i++) {
+      values[i].value = i - 1;        /* UNKNOWN is -1 */
+      values[i].value_name = g_strdup_printf ("%d", i - 1);
+      values[i].value_nick = g_strdup (snd_pcm_format_name ((snd_pcm_format_t) i - 1));
     }
+  }
 
-    if (!type) {
-        type = g_enum_register_static ("GstAlsaFormat", values);
-    }
-    return type;
+  if (!type)
+    type = g_enum_register_static ("GstAlsaFormat", values);
+
+  return type;
 }
 
 GType
 gst_alsa_get_type (void)
 {
-    static GType alsa_type = 0;
-
-    if (!alsa_type) {
-        static const GTypeInfo alsa_info = {
-            sizeof(GstAlsaClass),
-            NULL,
-            NULL,
-            NULL,
-            NULL,
-            NULL,
-            sizeof(GstAlsa),
-            0,
-            NULL,
-        };
-        alsa_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAlsa", &alsa_info, 0);
-    }
-    return alsa_type;
+  static GType alsa_type = 0;
+
+  if (!alsa_type) {
+    static const GTypeInfo alsa_info = {
+      sizeof (GstAlsaClass),
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      sizeof (GstAlsa),
+      0,
+      NULL,
+    };
+
+    alsa_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAlsa", &alsa_info, 0);
+  }
+  return alsa_type;
 }
 
 GType
 gst_alsa_sink_get_type (void)
 {
-    static GType alsa_type = 0;
-
-    if (!alsa_type) {
-        static const GTypeInfo alsa_info = {
-            sizeof(GstAlsaClass),
-            NULL,
-            NULL,
-            (GClassInitFunc)gst_alsa_class_init,
-            NULL,
-            NULL,
-            sizeof(GstAlsa),
-            0,
-            (GInstanceInitFunc)gst_alsa_init,
-        };
-        alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSink", &alsa_info, 0);
-    }
-    return alsa_type;
+  static GType alsa_type = 0;
+
+  if (!alsa_type) {
+    static const GTypeInfo alsa_info = {
+      sizeof (GstAlsaClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_alsa_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAlsa),
+      0,
+      (GInstanceInitFunc) gst_alsa_init,
+    };
+
+    alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSink", &alsa_info, 0);
+  }
+  return alsa_type;
 }
 
 GType
 gst_alsa_src_get_type (void)
 {
-    static GType alsa_type = 0;
-
-    if (!alsa_type) {
-        static const GTypeInfo alsa_info = {
-            sizeof(GstAlsaClass),
-            NULL,
-            NULL,
-            (GClassInitFunc)gst_alsa_class_init,
-            NULL,
-            NULL,
-            sizeof(GstAlsa),
-            0,
-            (GInstanceInitFunc)gst_alsa_init,
-        };
-        alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSrc", &alsa_info, 0);
-    }
-    return alsa_type;
+  static GType alsa_type = 0;
+
+  if (!alsa_type) {
+    static const GTypeInfo alsa_info = {
+      sizeof (GstAlsaClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_alsa_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAlsa),
+      0,
+      (GInstanceInitFunc) gst_alsa_init,
+    };
+
+    alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSrc", &alsa_info, 0);
+  }
+  return alsa_type;
 }
 
-static GstPadTemplate*
-gst_alsa_src_pad_factory(void)
-{
-    static GstPadTemplate *template = NULL;
+/*** GOBJECT FUNCTIONS ********************************************************/
 
-    if (!template)
-        template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES,
-                                        gst_caps_new("src", "audio/raw", NULL),
-                                        NULL);
+enum
+{
+  ARG_0,
+  ARG_DEVICE,
+  ARG_FORMAT,
+  ARG_CHANNELS,
+  ARG_RATE,
+  ARG_PERIODCOUNT,
+  ARG_PERIODFRAMES,
+  ARG_DEBUG,
+  ARG_AUTORECOVER
+};
 
-    return template;
-}
+static GstElement *parent_class = NULL;
 
-static GstPadTemplate*
-gst_alsa_src_request_pad_factory(void)
+static void
+gst_alsa_class_init (GstAlsaClass *klass)
 {
-    static GstPadTemplate *template = NULL;
-
-    if (!template)
-        template =
-            gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST,
-                                 gst_caps_new("src-request", "audio/raw",
-                                              gst_props_new("channels",
-                                                            GST_PROPS_INT(1),
-                                                            NULL)),
-                                        NULL);
-
-    return template;
+  GObjectClass *object_class;
+  GstElementClass *element_class;
+
+  object_class = (GObjectClass *) klass;
+  element_class = (GstElementClass *) klass;
+
+  if (parent_class == NULL)
+    parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  object_class->get_property = gst_alsa_get_property;
+  object_class->set_property = gst_alsa_set_property;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
+    g_param_spec_string ("device", "Device", "Alsa device, as defined in an asoundrc",
+                         "default", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FORMAT,
+    g_param_spec_enum ("format", "Format", "PCM audio format",
+                       GST_TYPE_ALSA_FORMAT, -1, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHANNELS,
+    g_param_spec_int ("channels", "Channels", "Number of channels",
+                      1, 64, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RATE,
+    g_param_spec_int ("rate", "Rate", "Sample rate, in Hz",
+                      8000, 192000, 44100, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PERIODCOUNT,
+    g_param_spec_int ("period-count", "Period count", "Number of hardware buffers to use",
+                      2, 64, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PERIODFRAMES,
+    g_param_spec_int ("period-frames", "Period frames", "Number of frames (samples on each channel) in one hardware period",
+                      64, 8192, 8192, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEBUG,
+    g_param_spec_boolean ("debug", "Debug", "Set to TRUE to output PCM state info",
+                          FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_AUTORECOVER,
+    g_param_spec_boolean ("autorecover", "Automatic xrun recovery", "Set to TRUE to increase the period count on xruns",
+                          TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  element_class->change_state = gst_alsa_change_state;
+  element_class->request_new_pad = gst_alsa_request_new_pad;
 }
 
-static GstPadTemplate*
-gst_alsa_sink_pad_factory(void)
+static void
+gst_alsa_init (GstAlsa *this)
 {
-    static GstPadTemplate *template = NULL;
+  /* init values */
+  this->handle = NULL;
 
-    if (!template)
-        template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES,
-                                        gst_caps_new("sink", "audio/raw", NULL),
-                                        NULL);
+  GST_FLAG_SET (this, GST_ELEMENT_THREAD_SUGGESTED);
 
-    return template;
-}
+  if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
+    this->stream = SND_PCM_STREAM_CAPTURE;
+    this->format = SND_PCM_FORMAT_S16;        /* native endian */
+    this->process = gst_alsa_src_process;
+    this->pads = g_list_append (NULL, g_new0 (GstAlsaPad, 1));
+    GST_ALSA_PAD (this->pads)->pad =
+      gst_pad_new_from_template (gst_alsa_src_pad_factory (), "src");
 
-static GstPadTemplate*
-gst_alsa_sink_request_pad_factory(void)
-{
-    static GstPadTemplate *template = NULL;
-
-    if (!template)
-        template =
-            gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST,
-                                 gst_caps_new("sink-request", "audio/raw",
-                                              gst_props_new("channels",
-                                                            GST_PROPS_INT(1),
-                                                            NULL)),
-                                 NULL);
-
-    return template;
+  } else if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SINK) {
+    this->stream = SND_PCM_STREAM_PLAYBACK;
+    this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are set */
+    this->process = gst_alsa_sink_process;
+    this->pads = g_list_append (NULL, g_new0 (GstAlsaPad, 1));
+    GST_ALSA_PAD (this->pads)->pad =
+      gst_pad_new_from_template (gst_alsa_sink_pad_factory (), "sink");
+
+  }
+
+  GST_ALSA_PAD (this->pads)->channel = -1;
+
+  /* data is interleaved by default, because there's only one default pad */
+  this->data_interleaved = TRUE;
+
+  gst_element_add_pad (GST_ELEMENT (this), GST_ALSA_PAD (this->pads)->pad);
+
+  gst_pad_set_link_function (GST_ALSA_PAD (this->pads)->pad, gst_alsa_link);
+  gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_loop);
 }
 
 static void
-gst_alsa_class_init(GstAlsaClass *klass)
+gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value,
+                       GParamSpec *pspec)
 {
-    GObjectClass *object_class;
-    GstElementClass *element_class;
-
-    object_class = (GObjectClass *)klass;
-    element_class = (GstElementClass *)klass;
-
-    if (parent_class == NULL)
-        parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
-
-    object_class->get_property = gst_alsa_get_property;
-    object_class->set_property = gst_alsa_set_property;
-
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE,
-                                    g_param_spec_string("device","Device","Alsa device, as defined in an asoundrc",
-                                                     "default",
-                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FORMAT,
-                                    g_param_spec_enum("format","Format","PCM audio format",
-                                                     GST_TYPE_ALSA_FORMAT, -1,
-                                                     G_PARAM_READWRITE));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
-                                    g_param_spec_int("channels","Channels","Number of channels",
-                                                     1, 64, 2,
-                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RATE,
-                                    g_param_spec_int("rate","Rate","Sample rate, in Hz",
-                                                     8000, 192000, 44100,
-                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PERIODCOUNT,
-                                    g_param_spec_int("period-count","Period count","Number of hardware buffers to use",
-                                                     2, 64, 2,
-                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PERIODFRAMES,
-                                    g_param_spec_int("period-frames","Period frames","Number of frames (samples on each channel) in one hardware period",
-                                                     64, 8192, 8192,
-                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEBUG,
-                                    g_param_spec_boolean("debug","Debug","Set to TRUE to output PCM state info",
-                                                     FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-    element_class->change_state = gst_alsa_change_state;
-
-    element_class->request_new_pad = gst_alsa_request_new_pad;
+  GstAlsa *this;
+
+  this = (GstAlsa *) object;
+  switch (prop_id) {
+  case ARG_DEVICE:
+    if (this->device)
+      g_free (this->device);
+    this->device = g_strdup (g_value_get_string (value));
+    break;
+  case ARG_FORMAT:
+    this->format = g_value_get_enum (value);
+    break;
+  case ARG_CHANNELS:
+    this->channels = g_value_get_int (value);
+    break;
+  case ARG_RATE:
+    this->rate = g_value_get_int (value);
+    break;
+  case ARG_PERIODCOUNT:
+    this->period_count = g_value_get_int (value);
+    this->buffer_frames = this->period_count * this->period_frames;
+    break;
+  case ARG_PERIODFRAMES:
+    this->period_frames = g_value_get_int (value);
+    this->buffer_frames = this->period_count * this->period_frames;
+    break;
+  case ARG_DEBUG:
+    this->debug = g_value_get_boolean (value);
+    return;
+  case ARG_AUTORECOVER:
+    this->autorecover = g_value_get_boolean (value);
+    return;
+  default:
+    GST_DEBUG (0, "Unknown arg");
+    return;
+  }
+
+  if (GST_STATE (this) == GST_STATE_NULL)
+    return;
+
+  if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) {
+    gst_alsa_stop_audio (this);
+    gst_alsa_set_params (this);
+    gst_alsa_start_audio (this);
+  } else {
+    gst_alsa_set_params (this);
+  }
 }
 
 static void
-gst_alsa_init(GstAlsa *this)
+gst_alsa_get_property (GObject *object, guint prop_id, GValue *value,
+                       GParamSpec *pspec)
 {
-    /* init values */
-    this->handle = NULL;
-
-    GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
-
-    if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SRC) {
-        this->stream = SND_PCM_STREAM_CAPTURE;
-        this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1));
-        GST_ALSA_PAD(this->pads)->pad = gst_pad_new_from_template(gst_alsa_src_pad_factory(), "src");
-        this->process = gst_alsa_src_process;
-        this->format = SND_PCM_FORMAT_S16; /* native endian */
-    } else if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SINK) {
-        this->stream = SND_PCM_STREAM_PLAYBACK;
-        this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1));
-        GST_ALSA_PAD(this->pads)->pad = gst_pad_new_from_template(gst_alsa_sink_pad_factory(), "sink");
-        this->process = gst_alsa_sink_process;
-        this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are
-                                                * set */
-    }
+  GstAlsa *this;
+
+  this = (GstAlsa *) object;
+
+  switch (prop_id) {
+  case ARG_DEVICE:
+    g_value_set_string (value, this->device);
+    break;
+  case ARG_FORMAT:
+    g_value_set_enum (value, this->format);
+    break;
+  case ARG_CHANNELS:
+    g_value_set_int (value, this->channels);
+    break;
+  case ARG_RATE:
+    g_value_set_int (value, this->rate);
+    break;
+  case ARG_PERIODCOUNT:
+    g_value_set_int (value, this->period_count);
+    break;
+  case ARG_PERIODFRAMES:
+    g_value_set_int (value, this->period_frames);
+    break;
+  case ARG_DEBUG:
+    g_value_set_boolean (value, this->debug);
+    break;
+  case ARG_AUTORECOVER:
+    g_value_set_boolean (value, this->autorecover);
+    break;
+  default:
+    GST_DEBUG (0, "Unknown arg");
+    break;
+  }
+}
 
-    GST_ALSA_PAD(this->pads)->channel = -1;
+/*** GSTREAMER PAD / STATE FUNCTIONS*******************************************/
 
-    this->data_interleaved = TRUE;
+static GstPadTemplate *
+gst_alsa_src_pad_factory (void)
+{
+  static GstPadTemplate *template = NULL;
 
-    gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad);
+  if (!template)
+    template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_SOMETIMES,
+                                     gst_caps_new ("src", "audio/raw", NULL),
+                                     NULL);
 
-    gst_pad_set_link_function(GST_ALSA_PAD(this->pads)->pad, gst_alsa_link);
-    gst_element_set_loop_function(GST_ELEMENT(this), gst_alsa_loop);
+  return template;
 }
 
-static GstPad*
-gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name) 
+static GstPadTemplate *
+gst_alsa_src_request_pad_factory (void)
 {
-    GstAlsa *this;
-    gint channel;
-    gchar *newname;
-    GList *l;
-    GstAlsaPad *pad;
-
-    g_return_val_if_fail ((this = GST_ALSA(element)), NULL);
-
-    /* you can't request a pad if the non-request pad is connected */
-    g_return_val_if_fail (this->data_interleaved == FALSE ||
-                          this->pads == NULL ||
-                          GST_ALSA_PAD(this->pads) == NULL ||
-                          GST_ALSA_PAD(this->pads)->pad == NULL ||
-                          GST_PAD_PEER(GST_ALSA_PAD(this->pads)->pad) == NULL,
-                          NULL);
-
-    if (name) {
-        channel = atoi (name + (strchr (templ->name_template, '%') - templ->name_template));
-
-        l = this->pads;
-        while (l) {
-            if (GST_ALSA_PAD(l)->channel == channel) {
-                g_warning("requested channel %d already in use.", channel);
-                return NULL;
-            }
-            l = l->next;
-        }
-        newname = g_strdup (name);
-    } else {
-        l = this->pads;
-        channel = 0;
-        while (l) {
-            if (GST_ALSA_PAD(l)->channel >= channel)
-                channel = GST_ALSA_PAD(l)->channel + 1;
-            l = l->next;
-        }
-        newname = g_strdup_printf (templ->name_template, channel);
-    }
+  static GstPadTemplate *template = NULL;
 
-    pad = g_new0(GstAlsaPad, 1);
-    pad->channel = channel;
-    pad->pad = gst_pad_new_from_template (templ, newname);
-    gst_element_add_pad (GST_ELEMENT (this), pad->pad);
-    gst_pad_set_link_function(pad->pad, gst_alsa_link);
-
-    if (this->data_interleaved && this->pads) {
-        gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD(this->pads)->pad);
-        g_free (GST_ALSA_PAD(this->pads));
-        g_list_free (this->pads);
-        this->pads = NULL;
-    }
+  if (!template)
+    template =
+      gst_pad_template_new ("src%d", GST_PAD_SRC, GST_PAD_REQUEST,
+                            gst_caps_new ("src-request", "audio/raw",
+                              gst_props_new ("channels", GST_PROPS_INT (1), NULL)),
+                            NULL);
 
-    this->pads = g_list_append(this->pads, pad);
+  return template;
+}
 
-    /* FIXME: allow interleaved access (for hw:N,M access on consumer hardware) */
+static GstPadTemplate *
+gst_alsa_sink_pad_factory (void)
+{
+  static GstPadTemplate *template = NULL;
 
-    if (this->data_interleaved) {
-        this->channels = pad->channel + 1;
-        this->data_interleaved = FALSE;
-    } else {
-        this->channels = MAX(this->channels, pad->channel + 1);
-    }
+  if (!template)
+    template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_SOMETIMES,
+                                     gst_caps_new ("sink", "audio/raw", NULL),
+                                     NULL);
 
-    return pad->pad;
+  return template;
 }
 
-static void
-gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+static GstPadTemplate *
+gst_alsa_sink_request_pad_factory (void)
 {
-    GstAlsa *this;
-
-    this = (GstAlsa *)object;
-    switch (prop_id) {
-    case ARG_DEVICE:
-        if (this->device)
-            g_free (this->device);
-        this->device = g_strdup(g_value_get_string (value));
-        break;
-    case ARG_FORMAT:
-        this->format = g_value_get_enum (value);
-        break;
-    case ARG_CHANNELS:
-        this->channels = g_value_get_int (value);
-        break;
-    case ARG_RATE:
-        this->rate = g_value_get_int (value);
-        break;
-    case ARG_PERIODCOUNT:
-        this->period_count = g_value_get_int (value);
-        this->buffer_frames = this->period_count * this->period_frames;
-        break;
-    case ARG_PERIODFRAMES:
-        this->period_frames = g_value_get_int (value);
-        this->buffer_frames = this->period_count * this->period_frames;
-        break;
-    case ARG_DEBUG:
-        this->debug = g_value_get_boolean (value);
-        return;
-    default:
-        GST_DEBUG(0, "Unknown arg");
-        return;
-    }
+  static GstPadTemplate *template = NULL;
 
-    if (GST_STATE(this) == GST_STATE_NULL)
-        return;
+  if (!template)
+    template =
+      gst_pad_template_new ("sink%d", GST_PAD_SINK, GST_PAD_REQUEST,
+                            gst_caps_new ("sink-request", "audio/raw",
+                              gst_props_new ("channels", GST_PROPS_INT (1), NULL)),
+                            NULL);
 
-    if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING)) {
-        gst_alsa_stop_audio(this);
-        gst_alsa_set_params(this);
-        gst_alsa_start_audio(this);
-    } else {
-        gst_alsa_set_params(this);
-    }
+  return template;
 }
 
-static void
-gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+static GstPad *
+gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ,
+                          const gchar *name)
 {
-    GstAlsa *this;
-
-    this = (GstAlsa *)object;
-
-    switch (prop_id) {
-    case ARG_DEVICE:
-        g_value_set_string (value, this->device);
-        break;
-    case ARG_FORMAT:
-        g_value_set_enum (value, this->format);
-        break;
-    case ARG_CHANNELS:
-        g_value_set_int (value, this->channels);
-        break;
-    case ARG_RATE:
-        g_value_set_int (value, this->rate);
-        break;
-    case ARG_PERIODCOUNT:
-        g_value_set_int (value, this->period_count);
-        break;
-    case ARG_PERIODFRAMES:
-        g_value_set_int (value, this->period_frames);
-        break;
-    case ARG_DEBUG:
-        g_value_set_boolean (value, this->debug);
-        break;
-    default:
-        GST_DEBUG(0, "Unknown arg");
-        break;
+  GstAlsa *this;
+  GstAlsaPad *pad;
+  gint channel;
+  gchar *newname;
+  GList *l;
+
+  g_return_val_if_fail ((this = GST_ALSA (element)), NULL);
+
+  /* you can't request a pad if the non-request pad is connected */
+  g_return_val_if_fail (this->data_interleaved == FALSE ||
+                        this->pads == NULL ||
+                        GST_ALSA_PAD (this->pads) == NULL ||
+                        GST_ALSA_PAD (this->pads)->pad == NULL ||
+                        GST_PAD_PEER (GST_ALSA_PAD (this->pads)->pad) == NULL,
+                        NULL);
+
+  if (name) {
+    /* locate the channel number in the requested pad name. to do so look at
+       where the % (which begins the %d) is in the template name. */
+    channel = atoi (name + (strchr (templ->name_template, '%') -
+                            templ->name_template));
+
+    /* make sure the requested channel is free. */
+    l = this->pads;
+    while (l) {
+      if (GST_ALSA_PAD (l)->channel == channel) {
+        g_warning ("requested channel %d already in use.", channel);
+        return NULL;
+      }
+      l = l->next;
     }
-}
+    newname = g_strdup (name);
+  } else {
+    channel = 0;
 
-static GstElementStateReturn
-gst_alsa_change_state(GstElement *element)
-{
-    GstAlsa *this;
-    guint chn;
-    GList *l;
-
-    g_return_val_if_fail(element != NULL, FALSE);
-    this = GST_ALSA (element);
-
-    switch (GST_STATE_PENDING(element)) {
-    case GST_STATE_NULL:
-        if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING))
-            gst_alsa_stop_audio(this);
-        if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN))
-            gst_alsa_close_audio(this);
-
-        l = this->pads;
-        while(l) {
-            if (GST_ALSA_PAD(l)->bs)
-                gst_bytestream_destroy(GST_ALSA_PAD(l)->bs);
-            l = l->next;
-        }
-
-        break;
-
-    case GST_STATE_READY:
-        break;
-
-    case GST_STATE_PAUSED:
-        if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE)
-            if (gst_alsa_open_audio(this) == FALSE)
-                return GST_STATE_FAILURE;
-        if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) {
-            if (this->stream == SND_PCM_STREAM_PLAYBACK) {
-                for (chn = 0; chn < this->channels; chn++) {
-                    gst_alsa_sink_silence_on_channel (this, chn, this->avail);
-                }
-            }
-            gst_alsa_stop_audio((GstAlsa *)element);
-        }
-        break;
-
-    case GST_STATE_PLAYING:
-        if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING) == FALSE)
-            if (gst_alsa_start_audio(this) == FALSE)
-                return GST_STATE_FAILURE;
-        break;
+    /* if the user doesn't care which channel, find the lowest channel number
+       that's free. */
+    l = this->pads;
+    while (l) {
+      if (GST_ALSA_PAD (l)->channel >= channel)
+        channel = GST_ALSA_PAD (l)->channel + 1;
+      l = l->next;
     }
-
-    if (GST_ELEMENT_CLASS(parent_class)->change_state)
-        return GST_ELEMENT_CLASS(parent_class)->change_state(element);
-
-    return GST_STATE_SUCCESS;
+    newname = g_strdup_printf (templ->name_template, channel);
+  }
+
+  /* set up a new GstAlsaPad struct to hold this channel's info. */
+  pad = g_new0 (GstAlsaPad, 1);
+  pad->channel = channel;
+  pad->pad = gst_pad_new_from_template (templ, newname);
+
+  gst_element_add_pad (GST_ELEMENT (this), pad->pad);
+  gst_pad_set_link_function (pad->pad, gst_alsa_link);
+
+  /* if the only pad is the default (nonrequest) pad, then remove the current
+     pad (we know it's free from the above g_return_val_if_fail) */
+  if (this->data_interleaved && this->pads) {
+    gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD (this->pads)->pad);
+    g_free (GST_ALSA_PAD (this->pads));
+    g_list_free (this->pads);
+    this->pads = NULL;
+  }
+
+  this->pads = g_list_append (this->pads, pad);
+
+  /* all request pads are mono (non-interleaved). FIXME: allow interleaved
+     access (for hw:N,M access on consumer hardware) */
+  if (this->data_interleaved) {
+    this->channels = pad->channel + 1;
+    this->data_interleaved = FALSE;
+  } else {
+    this->channels = MAX (this->channels, pad->channel + 1);
+  }
+
+  return pad->pad;
 }
 
 static gboolean
 gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps)
 {
-    gint law, endianness, width, depth, channels;
-    gboolean sign;
-    gint format = -1;
-    const gchar* format_name;
+  gint law, endianness, width, depth, channels;
+  gboolean sign;
+  gint format = -1;
+  const gchar *format_name;
 
-    if (!gst_caps_get_string (caps, "format", &format_name))
-        return FALSE;
+  if (!gst_caps_get_string (caps, "format", &format_name))
+    return FALSE;
 
-    if (format_name == NULL) {
-        return FALSE;
-    } else if (strcmp(format_name, "int")==0) {
-       if (!gst_caps_get (caps,
-                           "width", &width,
-                           "depth", &depth,
-                           "law", &law,
-                           "endianness", &endianness,
-                           "signed", &sign,
-                           NULL))
-            return FALSE;
-
-        if (law == 0) {
-            if (width == 8) {
-                if (sign == TRUE) {
-                    format = SND_PCM_FORMAT_S8;
-                } else {
-                    format = SND_PCM_FORMAT_U8;
-                }
-            } else if (width == 16) {
-                if (sign == TRUE) {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_S16_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_S16_BE;
-                } else {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_U16_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_U16_BE;
-                }
-            } else if (width == 24) {
-                if (sign == TRUE) {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_S24_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_S24_BE;
-                } else {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_U24_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_U24_BE;
-                }
-            } else if (width == 32) {
-                if (sign == TRUE) {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_S32_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_S32_BE;
-                } else {
-                    if (endianness == G_LITTLE_ENDIAN)
-                        format = SND_PCM_FORMAT_U32_LE;
-                    else if (endianness == G_BIG_ENDIAN)
-                        format = SND_PCM_FORMAT_U32_BE;
-                }
-            }
-        } else if (law == 1) { /* mu law */
-            if (width == depth && width == 8 && sign == FALSE) {
-                format = SND_PCM_FORMAT_MU_LAW;
-            } else {
-                return FALSE;
-            }
-        } else if (law == 2) { /* a law, ug. */
-            if (width == depth && width == 8 && sign == FALSE) {
-                format = SND_PCM_FORMAT_A_LAW;
-            }
+  if (format_name == NULL) {
+    return FALSE;
+  } else if (strcmp (format_name, "int") == 0) {
+    if (!gst_caps_get (caps,
+                       "width", &width, "depth", &depth, "law", &law,
+                       "endianness", &endianness, "signed", &sign, NULL))
+      return FALSE;
+
+    if (law == 0) {
+      if (width == 8) {
+        if (sign == TRUE) {
+          format = SND_PCM_FORMAT_S8;
         } else {
-            return FALSE;
+          format = SND_PCM_FORMAT_U8;
         }
-    } else if (strcmp(format_name, "float")==0) {
-       const gchar *layout;
-
-        if (!gst_caps_get_string (caps, "layout", &layout))
-            return FALSE;
-
-        if (strcmp(layout, "gfloat")==0) {
-            format = SND_PCM_FORMAT_FLOAT;
+      } else if (width == 16) {
+        if (sign == TRUE) {
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_S16_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S16_BE;
         } else {
-            return FALSE;
-            /* you need doubles? jeez... */
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_U16_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U16_BE;
         }
-    } else {
+      } else if (width == 24) {
+        if (sign == TRUE) {
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_S24_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S24_BE;
+        } else {
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_U24_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U24_BE;
+        }
+      } else if (width == 32) {
+        if (sign == TRUE) {
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_S32_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S32_BE;
+        } else {
+          if (endianness == G_LITTLE_ENDIAN)   format = SND_PCM_FORMAT_U32_LE;
+          else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U32_BE;
+        }
+      }
+    } else if (law == 1) { /* mu law */
+      if (width == depth && width == 8 && sign == FALSE) {
+        format = SND_PCM_FORMAT_MU_LAW;
+      } else {
         return FALSE;
+      }
+    } else if (law == 2) { /* a law, ug. */
+      if (width == depth && width == 8 && sign == FALSE) {
+        format = SND_PCM_FORMAT_A_LAW;
+      }
+    } else {
+      return FALSE;
     }
+  } else if (strcmp (format_name, "float") == 0) {
+    const gchar *layout;
 
-    this->format = format;
-    if (!gst_caps_get (caps,
-                       "rate", &this->rate,
-                       "channels", &channels,
-                       NULL))
-        return FALSE;
+    if (!gst_caps_get_string (caps, "layout", &layout))
+      return FALSE;
 
-    if (this->data_interleaved)
-        this->channels = channels;
-    else if (channels != 1)
-        return FALSE;
+    if (strcmp (layout, "gfloat") == 0) {
+      format = SND_PCM_FORMAT_FLOAT;
+    } else {
+      return FALSE;
+      /* you need doubles? jeez... */
+    }
+  } else {
+    return FALSE;
+  }
+
+  this->format = format;
+  if (!gst_caps_get (caps, "rate", &this->rate, "channels", &channels, NULL))
+    return FALSE;
 
-    return TRUE;
+  if (this->data_interleaved)
+    this->channels = channels;
+  else if (channels != 1)
+    return FALSE;
+
+  return TRUE;
 }
 
 /* caps are so painful sometimes. */
-static GstCaps*
+static GstCaps *
 gst_alsa_caps (GstAlsa *this)
 {
-    gint law, endianness, width, depth;
-    gboolean sign;
-    GstProps *props;
-
-    g_return_val_if_fail (this != NULL && this->handle != NULL, NULL);
-
-    if (this->format == SND_PCM_FORMAT_FLOAT) {
-        props = gst_props_new ("format", GST_PROPS_STRING ("float"),
-                               "layout", GST_PROPS_STRING ("gfloat"),
-                               "rate",       GST_PROPS_INT (this->rate),
-                               "channels",   GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
-                               NULL);
+  gint law, endianness, width, depth;
+  gboolean sign;
+  GstProps *props;
+
+  g_return_val_if_fail (this != NULL && this->handle != NULL, NULL);
+
+  if (this->format == SND_PCM_FORMAT_FLOAT) {
+    props = gst_props_new ("format",   GST_PROPS_STRING ("float"),
+                           "layout",   GST_PROPS_STRING ("gfloat"),
+                           "rate",     GST_PROPS_INT (this->rate),
+                           "channels", GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
+                           NULL);
+  } else {
+    /* we'll just have to assume int, i don't feel like checking */
+    if (this->format == SND_PCM_FORMAT_MU_LAW) {
+      law = 1;
+      width = 8; sign = FALSE; endianness = 0;
+    } else if (this->format == SND_PCM_FORMAT_A_LAW) {
+      law = 2;
+      width = 8; sign = FALSE; endianness = 0;
     } else {
-        /* we'll just have to assume int, i don't feel like checking */
-        if (this->format == SND_PCM_FORMAT_MU_LAW) {
-            law = 1;
-            width = 8;
-            sign = FALSE;
-            endianness = 0;
-        } else if (this->format == SND_PCM_FORMAT_A_LAW) {
-            law = 2;
-            width = 8;
-            sign = FALSE;
-            endianness = 0;
-        } else {
-            law = 0;
-            if (this->format == SND_PCM_FORMAT_S8) {
-                width = 8;
-                sign = TRUE;
-                endianness = 0;
-            } else if (this->format == SND_PCM_FORMAT_U8) {
-                width = 8;
-                sign = FALSE;
-                endianness = 0;
-            } else if (this->format == SND_PCM_FORMAT_S16_LE) {
-                width = 16;
-                sign = TRUE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_S16_BE) {
-                width = 16;
-                sign = TRUE;
-                endianness = G_BIG_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U16_LE) {
-                width = 16;
-                sign = FALSE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U16_BE) {
-                width = 16;
-                sign = FALSE;
-                endianness = G_BIG_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_S24_LE) {
-                width = 24;
-                sign = TRUE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_S24_BE) {
-                width = 24;
-                sign = TRUE;
-                endianness = G_BIG_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U24_LE) {
-                width = 24;
-                sign = FALSE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U24_BE) {
-                width = 24;
-                sign = FALSE;
-                endianness = G_BIG_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_S32_LE) {
-                width = 32;
-                sign = TRUE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_S32_BE) {
-                width = 32;
-                sign = TRUE;
-                endianness = G_BIG_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U32_LE) {
-                width = 32;
-                sign = FALSE;
-                endianness = G_LITTLE_ENDIAN;
-            } else if (this->format == SND_PCM_FORMAT_U32_BE) {
-                width = 32;
-                sign = FALSE;
-                endianness = G_BIG_ENDIAN;
-            } else {
-                g_error ("what is going on here?");
-                return NULL;
-            }
-        }
-        depth = width;
-        props = gst_props_new ("format", GST_PROPS_STRING ("int"),
-                               "rate",       GST_PROPS_INT (this->rate),
-                               "channels",   GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
-                               "law",        GST_PROPS_INT (law),
-                               "endianness", GST_PROPS_INT (endianness),
-                               "signed",     GST_PROPS_BOOLEAN (sign),
-                               "width",      GST_PROPS_INT (width),
-                               "depth",      GST_PROPS_INT (depth),
-                               NULL);
+      law = 0;
+      if (this->format == SND_PCM_FORMAT_S8) {
+        width = 8; sign = TRUE; endianness = 0;
+      } else if (this->format == SND_PCM_FORMAT_U8) {
+        width = 8; sign = FALSE; endianness = 0;
+      } else if (this->format == SND_PCM_FORMAT_S16_LE) {
+        width = 16; sign = TRUE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_S16_BE) {
+        width = 16; sign = TRUE; endianness = G_BIG_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U16_LE) {
+        width = 16; sign = FALSE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U16_BE) {
+        width = 16; sign = FALSE; endianness = G_BIG_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_S24_LE) {
+        width = 24; sign = TRUE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_S24_BE) {
+        width = 24; sign = TRUE; endianness = G_BIG_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U24_LE) {
+        width = 24; sign = FALSE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U24_BE) {
+        width = 24; sign = FALSE; endianness = G_BIG_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_S32_LE) {
+        width = 32; sign = TRUE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_S32_BE) {
+        width = 32; sign = TRUE; endianness = G_BIG_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U32_LE) {
+        width = 32; sign = FALSE; endianness = G_LITTLE_ENDIAN;
+      } else if (this->format == SND_PCM_FORMAT_U32_BE) {
+        width = 32; sign = FALSE; endianness = G_BIG_ENDIAN;
+      } else {
+        g_error ("Unknown audio format %u", this->format);
+        return NULL;
+      }
     }
-
-    return gst_caps_new ("alsasrc", "audio/raw", props);
+    depth = width;
+    props = gst_props_new ("format",     GST_PROPS_STRING ("int"),
+                           "rate",       GST_PROPS_INT (this->rate),
+                           "channels",   GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
+                           "law",        GST_PROPS_INT (law),
+                           "endianness", GST_PROPS_INT (endianness),
+                           "signed",     GST_PROPS_BOOLEAN (sign),
+                           "width",      GST_PROPS_INT (width),
+                           "depth",      GST_PROPS_INT (depth),
+                           NULL);
+  }
+
+  return gst_caps_new ("alsasrc", "audio/raw", props);
 }
 
-/*
- * Negotiates the caps, "borrowed" from gstosssink.c
- */
+/* Negotiates the caps, "borrowed" from gstosssink.c */
 GstPadLinkReturn
-gst_alsa_link(GstPad *pad, GstCaps *caps)
+gst_alsa_link (GstPad *pad, GstCaps *caps)
 {
-    GstAlsa *this;
-    gboolean need_mmap;
+  GstAlsa *this;
+  gboolean need_mmap;
 
-    g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED);
-    g_return_val_if_fail (pad  != NULL, GST_PAD_LINK_REFUSED);
+  g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED);
+  g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
 
-    this = GST_ALSA(gst_pad_get_parent(pad));
+  this = GST_ALSA (gst_pad_get_parent (pad));
 
-    if (GST_CAPS_IS_FIXED (caps)) {
-        if (this->handle == NULL)
-            if (!gst_alsa_open_audio(this))
-                return GST_PAD_LINK_REFUSED;
+  if (GST_CAPS_IS_FIXED (caps)) {
+    if (this->handle == NULL)
+      if (!gst_alsa_open_audio (this))
+        return GST_PAD_LINK_REFUSED;
 
-        if (gst_alsa_parse_caps(this, caps)) {
-            need_mmap = this->mmap_open;
+    if (gst_alsa_parse_caps (this, caps)) {
+      need_mmap = this->mmap_open;
 
-            /* sync the params */
-            if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING))
-                gst_alsa_stop_audio(this);
+      /* sync the params */
+      if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this);
+      if (GST_FLAG_IS_SET (this, GST_ALSA_OPEN))    gst_alsa_close_audio (this);
 
-            if (GST_FLAG_IS_SET(this, GST_ALSA_OPEN))
-                gst_alsa_close_audio(this);
+      /* FIXME send out another caps if nego fails */
 
-            /* FIXME send out another caps if nego fails */
+      if (!gst_alsa_open_audio (this))  return GST_PAD_LINK_REFUSED;
+      if (!gst_alsa_start_audio (this)) return GST_PAD_LINK_REFUSED;
 
-            if (!gst_alsa_open_audio(this))
-                return GST_PAD_LINK_REFUSED;
+      if (need_mmap && !gst_alsa_get_channel_addresses (this))
+        return GST_PAD_LINK_REFUSED;
 
-            if (!gst_alsa_start_audio(this))
-                return GST_PAD_LINK_REFUSED;
+      return GST_PAD_LINK_OK;
+    }
 
-            if (need_mmap && !gst_alsa_get_channel_addresses(this))
-                return GST_PAD_LINK_REFUSED;
+    return GST_PAD_LINK_REFUSED;
+  }
 
-            return GST_PAD_LINK_OK;
-        }
+  return GST_PAD_LINK_DELAYED;
+}
 
-        return GST_PAD_LINK_REFUSED;
+static GstElementStateReturn
+gst_alsa_change_state (GstElement *element)
+{
+  GstAlsa *this;
+  GList *l;
+
+  g_return_val_if_fail (element != NULL, FALSE);
+  this = GST_ALSA (element);
+
+  switch (GST_STATE_PENDING (element)) {
+  case GST_STATE_NULL:
+    if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this);
+    if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN))    gst_alsa_close_audio (this);
+
+    /* clear out bytestreams as well. */
+    l = this->pads;
+    while (l) {
+      if (GST_ALSA_PAD (l)->bs)
+        gst_bytestream_destroy (GST_ALSA_PAD (l)->bs);
+      l = l->next;
     }
 
-    return GST_PAD_LINK_DELAYED;
+    break;
+
+  case GST_STATE_READY:
+    break;
+
+  case GST_STATE_PAUSED:
+    if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN) == FALSE)
+      if (!gst_alsa_open_audio (this))
+        return GST_STATE_FAILURE;
+
+    if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING))
+      gst_alsa_stop_audio (this);
+
+    break;
+
+  case GST_STATE_PLAYING:
+    if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) == FALSE)
+      if (!gst_alsa_start_audio (this))
+        return GST_STATE_FAILURE;
+
+    break;
+  }
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state)
+    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  return GST_STATE_SUCCESS;
 }
 
+/*** AUDIO PROCESSING *********************************************************/
+
 /* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks,
    paul! */
 static void
 gst_alsa_loop (GstElement *element)
 {
-    guint32 i;
-    GstAlsa *this = GST_ALSA(element);
-
-    g_return_if_fail(this != NULL);
-
-    do {
-        if (snd_pcm_wait (this->handle, 1000) < 0) {
-            if (errno == EINTR) {
-                /* this happens mostly when run
-                 * under gdb, or when exiting due to a signal */
-                g_print ("EINTR\n");
-                if (gst_element_interrupt (element))
-                    break;
-                else
-                    continue;
-            }
-
-            g_warning("error waiting for alsa pcm: (%d: %s)", errno, strerror(errno));
-            return;
-        }
+  GstAlsa *this = GST_ALSA (element);
+
+  g_return_if_fail (this != NULL);
+
+  while (1) {
+    if (snd_pcm_wait (this->handle, 1000) < 0) {
+      if (errno == EINTR) {
+        /* this happens mostly when run
+         * under gdb, or when exiting due to a signal */
+        g_print ("EINTR\n");
+        if (gst_element_interrupt (element))
+          break;
+        else
+          continue;
+      }
+
+      g_warning ("error waiting for alsa pcm: (%d: %s)", errno, strerror (errno));
+      return;
+    }
 
-        this->avail = snd_pcm_avail_update (this->handle);
-        DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
-
-        if (this->avail < 0) {
-            if (this->avail == -EPIPE) {
-                gst_alsa_xrun_recovery (this);
-                this->avail = 0;
-            } else {
-                g_warning("unknown ALSA avail_update return value (%d)",
-                          (int)this->avail);
-                return;
-            }
-        }
+    this->avail = snd_pcm_avail_update (this->handle);
+    DEBUG ("snd_pcm_avail_update() = %d", (int) this->avail);
+
+    if (this->avail < 0) {
+      if (this->avail == -EPIPE) {
+        gst_alsa_xrun_recovery (this);
+        this->avail = 0;
+      } else {
+        g_warning ("unknown ALSA avail_update return value (%d)", (int) this->avail);
+        return;
+      }
+    }
 
-        /* round down to nearest period_frames avail */
-        this->avail -= this->avail % this->period_frames;
-
-        DEBUG ("snd_pcm_avail_update(), rounded down = %d", (int)this->avail);
-
-        /* we need to loop here because the available bytes might not be
-         * contiguous */
-        while (this->avail) {
-            /* changes this->avail, as a side effect */
-            if (!gst_alsa_get_channel_addresses (this) < 0) {
-                g_error("could not get channels");
-                return;
-            }
-
-            if (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK) {
-                for (i = 0; i < this->channels; i++) {
-                    if (this->mute & (1<<i)) {
-                        gst_alsa_sink_silence_on_channel (this, i, this->buffer_frames);
-                    }
-                }
-            }
-
-            if (!this->process(this, this->avail)) {
-                g_warning("alsa: something happened while processing audio");
-                return;
-            }
-
-            /* we could have released the mmap regions on a state change */
-            if (this->mmap_open)
-                gst_alsa_release_channel_addresses(this);
-        }
+    /* round down to nearest period_frames avail */
+    this->avail -= this->avail % this->period_frames;
 
-       if (gst_element_interrupt (element))
-            break;
+    DEBUG ("snd_pcm_avail_update(), rounded down = %d", (int) this->avail);
 
-    } while (TRUE);
-}
+    /* the available bytes might not be contiguous */
+    while (this->avail > 0) {
+      if (!gst_alsa_get_channel_addresses (this)) {
+        g_error ("could not get channels");
+        return;
+      }
 
-static gboolean
-gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
-{
-    GstBuffer *buf;
-    GList *l;
-    GstAlsaPad *pad = NULL;
-    GstCaps *caps;
-    gint unit;
-
-    static gboolean caps_set = FALSE;
-
-    if (!caps_set) {
-        /* let's get on the caps-setting merry-go-round! */
-        caps = gst_alsa_caps(this);
-        l = this->pads;
-        while (l) {
-            if (gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps) <= 0) {
-                g_print ("setting caps (%p) in source (%p) failed\n", caps, this);
-                sleep(1);
-                return FALSE;
-            }
-
-            l = l->next;
+      if (! (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK))
+        if (!this->process (this, this->avail)) {
+          g_warning ("alsa: something happened while processing audio");
+          return;
         }
-        caps_set = TRUE;
-    }
 
-    unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
-
-    while (frames) {
-        l = this->pads;
-        while (l) {
-            pad = GST_ALSA_PAD(l);
-
-            if (!pad->buf) {
-                pad->buf = g_malloc(this->period_frames * unit);
-                /*  g_print ("created buffer %p of size %d\n", pad->buf, this->period_frames * unit); */
-            }
-            /*
-            g_print ("pad->buf = %p, offset = %d\n", pad->buf, pad->offset);
-            g_print ("about to memcpy(%p, %p, %d)\n",
-                     pad->buf + pad->offset * unit,
-                     pad->access_addr,
-                     MIN(frames, this->period_frames - pad->offset) * unit);
-            */
-            memcpy(pad->buf + pad->offset * unit, pad->access_addr,
-                   MIN(frames, this->period_frames - pad->offset) * unit);
-
-            pad->offset += MIN(frames, this->period_frames - pad->offset);
-
-            if (pad->offset >= this->period_frames) {
-               g_assert(pad->offset <= this->period_frames);
-                buf = gst_buffer_new();
-                GST_BUFFER_DATA(buf)    = pad->buf;
-                GST_BUFFER_SIZE(buf)    = this->period_frames * unit;
-                GST_BUFFER_MAXSIZE(buf) = this->period_frames * unit;
-                gst_pad_push(pad->pad, buf);
-                pad->buf = NULL;
-                pad->offset = 0;
-            }
-            l = l->next;
-        }
-        frames -= MIN(frames, this->period_frames - pad->offset); /* shouldn't */
-        /* matter which pad, in theory (tm) */
+      /* releasing channel addresses sets this->avail to 0. */
+      if (this->mmap_open)
+        gst_alsa_release_channel_addresses (this);
     }
 
-    return TRUE;
+    if (gst_element_interrupt (element))
+      break;
+  }
 }
 
-static gboolean
-gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
+static void
+gst_alsa_xrun_recovery (GstAlsa *this)
 {
-    guint8 *peeked;
-    guint32 len, avail, num_peeked = 0;
-    GstEvent *event = NULL;
-    GstAlsaPad *alsa_pad;
-    GList *l;
+  snd_pcm_status_t *status;
+  gint err;
 
-    /* this is necessary because the sample_bytes will change, probably, when
-       caps are set, which will occur after the first bytestream_peek. we
-       underestimate the amount of data we will need by peeking 1 byte only. */
+  snd_pcm_status_alloca (&status);
 
-    if (!this->sample_bytes) {
-        alsa_pad = GST_ALSA_PAD(this->pads);
-
-        if (! alsa_pad->bs)
-            alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
-
-               while (num_peeked == 0) {
-               num_peeked = gst_bytestream_peek_bytes (alsa_pad->bs, &peeked, frames);
-               if (num_peeked == 0) {
-               gst_bytestream_get_status(alsa_pad->bs, &avail, &event);
-                   if (event) {
-                   if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
-                           /* really, we should just cut this pad out of the graph. let
-                          me know when this is needed ;) */
-
-                           gst_element_set_eos(GST_ELEMENT(this));
-                       gst_event_unref(event);
-                           return TRUE;
-                                       } else {
-                           g_warning("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE(event));
-                                       }
-               } else {
-                       /* the element at the top of the chain did not emit an eos
-                    * event. this is a Bug(tm) */
-                       g_assert_not_reached();
-                               }
-                       } else if (num_peeked < frames) {
-                   g_warning("could not make initial pull of %d bytes on pad %s:%s",
-                         (int)frames,
-                             GST_DEBUG_PAD_NAME(alsa_pad->pad));
-                   gst_element_set_eos (GST_ELEMENT(this));
-               return FALSE;
-               }
-               }
-
-        if (!this->sample_bytes) {
-            g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
-            return FALSE;
-        }
-    }
+  if ((err = snd_pcm_status (this->handle, status)) < 0)
+    g_warning ("status error: %s", snd_strerror (err));
 
-    len = frames * this->channels * this->sample_bytes;
+  if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) {
+    struct timeval now, diff, tstamp;
 
-    l = this->pads;
-    while (l) {
-        alsa_pad = GST_ALSA_PAD(l);
-
-        if (! alsa_pad->bs)
-            alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
-
-        num_peeked = gst_bytestream_peek_bytes(alsa_pad->bs, &peeked, len);
-        if (num_peeked == 0) {
-            gst_bytestream_get_status(alsa_pad->bs, &avail, &event);
-            if (event) {
-                if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
-                    /* really, we should just cut this pad out of the graph. let
-                       me know when this is needed ;) */
-
-                    gst_element_set_eos(GST_ELEMENT(this));
-                    gst_event_unref(event);
-                    return TRUE;
-                } else {
-                       g_warning("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE(event));
-                               }
-            } else {
-                /* the element at the top of the chain did not emit an eos
-                 * event. this is a Bug(tm) */
-                g_assert_not_reached();
-            }
-        } else if (peeked && alsa_pad->access_addr) {
-            memcpy(alsa_pad->access_addr, peeked, num_peeked);
-            gst_bytestream_flush(alsa_pad->bs, num_peeked);
-        } else {
-            g_warning ("error while writing %u bytes (peeked into %p) to %p",
-                       num_peeked, peeked, alsa_pad->access_addr);
-        }
+    gettimeofday (&now, 0);
+    snd_pcm_status_get_trigger_tstamp (status, &tstamp);
+    timersub (&now, &tstamp, &diff);
+    g_warning ("alsa: xrun of at least %.3f msecs", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
+  }
 
-        l = l->next;
-    }
+  gst_alsa_stop_audio (this);
 
-    return TRUE;
+  /* increase the period count to prevent further xruns (at the cost of
+     increased latency and memory usage). only do this if it's allowed. */
+  if ((this->period_count <= 32) && this->autorecover) {
+    this->period_count *= 2;
+    this->buffer_frames = this->period_count * this->period_frames;
+    gst_alsa_set_params(this);
+  }
+
+  gst_alsa_start_audio (this);
 }
 
-static void
-gst_alsa_xrun_recovery (GstAlsa *this)
+static gboolean
+gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
-    snd_pcm_status_t *status;
-    int res;
-
-    snd_pcm_status_alloca(&status);
+  GstAlsaPad *pad = NULL;
+  GstBuffer *buf;
+  GstCaps *caps;
+  GList *l;
+  guint32 channel;
+  gint unit;
+
+  static gboolean caps_set = FALSE;
+
+  /* let's get on the caps-setting merry-go-round! */
+  if (!caps_set) {
+    caps = gst_alsa_caps (this);
+    l = this->pads;
+    while (l) {
+      if (gst_pad_try_set_caps (GST_ALSA_PAD (l)->pad, caps) <= 0) {
+        g_print ("setting caps (%p) in source (%p) failed\n", caps, this);
+        sleep (1);
+        return FALSE;
+      }
 
-    if ((res = snd_pcm_status(this->handle, status)) < 0) {
-        g_warning ("status error: %s", snd_strerror(res));
+      l = l->next;
     }
+    caps_set = TRUE;
+  }
+
+  unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
 
-    if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
-        struct timeval now, diff, tstamp;
-        gettimeofday(&now, 0);
-        snd_pcm_status_get_trigger_tstamp(status, &tstamp);
-        timersub(&now, &tstamp, &diff);
-        g_warning("alsa: xrun of at least %.3f msecs", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
+  /* pull the same amount of data into each pad. create an outgoing buffer with
+     the data, and push the buffer. lather, rinse, repeat. */
+  while (frames) {
+    l = this->pads;
+    while (l) {
+      pad = GST_ALSA_PAD (l);
+
+      if (!pad->buf)
+        pad->buf = g_malloc (this->period_frames * unit);
+
+      channel = (pad->channel > 0) ? pad->channel - 1 : 0;
+      memcpy (pad->buf + pad->offset * unit, this->access_addr[channel],
+              MIN (frames, this->period_frames - pad->offset) * unit);
+
+      pad->offset += MIN (frames, this->period_frames - pad->offset);
+
+      if (pad->offset >= this->period_frames) {
+        g_assert (pad->offset <= this->period_frames);
+        buf = gst_buffer_new ();
+        GST_BUFFER_DATA (buf) = pad->buf;
+        GST_BUFFER_SIZE (buf) = this->period_frames * unit;
+        GST_BUFFER_MAXSIZE (buf) = this->period_frames * unit;
+        gst_pad_push (pad->pad, buf);
+        pad->buf = NULL;
+        pad->offset = 0;
+      }
+      l = l->next;
     }
+    /* all pads should have the same amount of data, shouldn't matter which one
+       we use for updating output frames ... */
+    frames -= MIN (frames, this->period_frames - pad->offset);
+  }
 
-    gst_alsa_stop_audio (this);
-    gst_alsa_start_audio (this);
+  return TRUE;
 }
 
-/* taken more or less from pbd's audioengine code */
 static gboolean
-gst_alsa_set_params (GstAlsa *this)
+gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
-    snd_pcm_sw_params_t *sw_param;
-    snd_pcm_hw_params_t *hw_param;
-    snd_pcm_access_mask_t *mask;
-    gint ret;
-
-    g_return_val_if_fail(this != NULL, FALSE);
-    g_return_val_if_fail(this->handle != NULL, FALSE);
-
-    DEBUG("Preparing channel: %s %dHz, %d channels\n",
-            snd_pcm_format_name(this->format),
-            this->rate, this->channels);
-
-    snd_pcm_hw_params_alloca(&hw_param);
-    snd_pcm_sw_params_alloca(&sw_param);
-
-    ret = snd_pcm_hw_params_any(this->handle, hw_param);
-    if (ret < 0) {
-        g_warning("Broken configuration for this PCM: no configurations available");
+  guint8 *peeked;
+  guint32 len, channel, num_peeked = 0;
+  gboolean muted;
+  GstAlsaPad *pad;
+  GList *l;
+
+  /* we need to check sample bytes here because the sample_bytes will change,
+     probably, when caps are set, which will occur after the first
+     bytestream_peek. we underestimate the amount of data we will need by
+     peeking "frames" bytes only. */
+
+  if (!this->sample_bytes) {
+    pad = GST_ALSA_PAD (this->pads);
+
+    if (!pad->bs)
+      pad->bs = gst_bytestream_new (pad->pad);
+
+    while (num_peeked == 0) {
+      num_peeked = gst_bytestream_peek_bytes (pad->bs, &peeked, frames);
+      if (num_peeked == 0) {
+        gst_alsa_sink_check_event (this, pad);
+      } else if (num_peeked < frames) {
+        g_warning ("could not make initial pull of %d bytes on pad %s:%s",
+                   (int) frames, GST_DEBUG_PAD_NAME (pad->pad));
+        gst_element_set_eos (GST_ELEMENT (this));
         return FALSE;
+      }
     }
 
-    if ((ret = snd_pcm_hw_params_set_periods_integer (this->handle, hw_param)) < 0) {
-        g_warning("cannot restrict period size to integral value.");
-        return FALSE;
+    if (!this->sample_bytes) {
+      g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
+      return FALSE;
     }
+  }
 
-    mask = alloca(snd_pcm_access_mask_sizeof());
-    snd_pcm_access_mask_none(mask);
+  len = frames * this->channels * this->sample_bytes;
 
-    if (this->data_interleaved) {
-       snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
-       } else {
-       snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
-       }
-    ret = snd_pcm_hw_params_set_access_mask(this->handle, hw_param, mask);
-    if (ret < 0) {
-        g_warning("the gstreamer alsa plugin does not support your hardware.");
-        return FALSE;
-    }
+  /* write len bytes out to each of our pads' mmap_area pointers. */
+  l = this->pads;
+  while (l) {
+    pad = GST_ALSA_PAD (l);
 
-    if (this->format != SND_PCM_FORMAT_UNKNOWN) {
-        ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format);
-        if (ret < 0) {
-            g_warning("Sample format (%s) not available: %s", snd_pcm_format_name(this->format), snd_strerror(ret));
-            return FALSE;
-        }
-        this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
-    }
+    channel = (pad->channel > 0) ? pad->channel - 1 : 0;
+    muted = (1 << channel) && this->mute;
 
-    ret = snd_pcm_hw_params_set_channels(this->handle, hw_param, this->channels);
-    if (ret < 0) {
-        g_warning("Channels count (%d) not available: %s", this->channels, snd_strerror(ret));
-        return FALSE;
-    }
-    this->channels = snd_pcm_hw_params_get_channels(hw_param);
+    if (!pad->bs)
+      pad->bs = gst_bytestream_new (pad->pad);
 
-    if (this->rate) {
-        ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
-        if (ret < 0) {
-            g_warning("error setting rate (%d): %s", this->rate, snd_strerror(ret));
-            return FALSE;
-        }
+    num_peeked = gst_bytestream_peek_bytes (pad->bs, &peeked, len);
+    if (num_peeked == 0) {
+      gst_alsa_sink_check_event (this, pad);
+    } else if (peeked && this->access_addr[channel] && (! muted)) {
+      memcpy (this->access_addr[channel], peeked, num_peeked);
+      gst_bytestream_flush (pad->bs, num_peeked);
+    } else {
+      g_warning ("error while writing %u bytes (peeked into %p) to %p",
+                 num_peeked, peeked, this->access_addr[channel]);
     }
 
-    if (this->period_count) {
-        ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
-        if (ret < 0) {
-            g_warning("error setting period count minimum (%d): %s", this->period_count, snd_strerror(ret));
-            return FALSE;
-        }
-    }
+    l = l->next;
+  }
 
-    if (this->period_frames) {
-        ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
-        if (ret < 0) {
-            g_warning("error setting period in frames (%d): %s", this->period_frames, snd_strerror(ret));
-            return FALSE;
-        }
-    }
+  return TRUE;
+}
 
-    if (this->buffer_frames) {
-        ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
-        if (ret < 0) {
-            g_warning("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror(ret));
-            return FALSE;
-        }
-    }
+static void
+gst_alsa_sink_check_event (GstAlsa *this, GstAlsaPad *pad)
+{
+  GstEvent *event = NULL;
+  guint8 *peeked;
+  guint32 avail, channel, num_peeked = 0;
 
-    ret = snd_pcm_hw_params(this->handle, hw_param);
-    if (ret < 0) {
-        g_warning("could not set hw params: %s", snd_strerror(ret));
-        snd_pcm_hw_params_dump(hw_param, this->out);
-        return FALSE;
-    }
+  gst_bytestream_get_status (pad->bs, &avail, &event);
 
-    if (!this->rate)
-        this->rate = snd_pcm_hw_params_get_rate(hw_param, 0);
-    if (!this->format)
-        this->format = snd_pcm_hw_params_get_format(hw_param);
-    if (!this->period_count)
-        this->period_count = snd_pcm_hw_params_get_periods(hw_param, 0);
-    if (!this->period_frames)
-        this->period_frames = snd_pcm_hw_params_get_period_size(hw_param, 0);
-    if (!this->buffer_frames)
-        this->buffer_frames = snd_pcm_hw_params_get_buffer_size(hw_param);
-    if (this->buffer_frames != this->period_count * this->period_frames)
-        g_critical ("buffer size != period size * number of periods, unexpected things may happen!");
-
-    snd_pcm_sw_params_current (this->handle, sw_param);
-
-    ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U);
-    if (ret < 0) {
-        g_warning("could not set start mode: %s", snd_strerror(ret));
-        return FALSE;
-    }
+  if (event) {
+    if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+      channel = (pad->channel > 0) ? pad->channel - 1 : 0;
+      num_peeked = gst_bytestream_peek_bytes (pad->bs, &peeked, avail);
+      if (num_peeked && peeked)
+        memcpy (this->access_addr[channel], peeked, num_peeked);
 
-    ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
-    if (ret < 0) {
-        g_warning("could not set stop mode: %s", snd_strerror(ret));
-        return FALSE;
+      gst_element_set_eos (GST_ELEMENT (this));
+      gst_event_unref (event);
+
+      /* FIXME: remove this alsa pad from the element */
+    } else {
+      g_warning ("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE (event));
     }
+  } else {
+    /* the element at the top of the chain did not emit an eos event. */
+    g_assert_not_reached ();
+  }
+}
 
-    ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0);
+/*** AUDIO SETUP / START / STOP ***********************************************/
+
+/* taken more or less from pbd's audioengine code */
+static gboolean
+gst_alsa_set_params (GstAlsa *this)
+{
+  snd_pcm_sw_params_t *sw_param;
+  snd_pcm_hw_params_t *hw_param;
+  snd_pcm_access_mask_t *mask;
+  gint ret;
+
+  g_return_val_if_fail (this != NULL, FALSE);
+  g_return_val_if_fail (this->handle != NULL, FALSE);
+
+  g_print ("Preparing channel: %s %dHz, %d channels\n",
+           snd_pcm_format_name (this->format), this->rate, this->channels);
+
+  snd_pcm_hw_params_alloca (&hw_param);
+  snd_pcm_sw_params_alloca (&sw_param);
+
+  ret = snd_pcm_hw_params_any (this->handle, hw_param);
+  if (ret < 0) {
+    g_warning ("Broken configuration for this PCM: no configurations available");
+    return FALSE;
+  }
+
+  if ((ret = snd_pcm_hw_params_set_periods_integer (this->handle, hw_param)) < 0) {
+    g_warning ("cannot restrict period size to integral value.");
+    return FALSE;
+  }
+
+  mask = alloca (snd_pcm_access_mask_sizeof ());
+  snd_pcm_access_mask_none (mask);
+
+  if (this->data_interleaved)
+    snd_pcm_access_mask_set (mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
+
+  snd_pcm_access_mask_set (mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+  ret = snd_pcm_hw_params_set_access_mask (this->handle, hw_param, mask);
+  if (ret < 0) {
+    g_warning ("the gstreamer alsa plugin does not support your hardware.");
+    return FALSE;
+  }
+
+  if (this->format != SND_PCM_FORMAT_UNKNOWN) {
+    ret = snd_pcm_hw_params_set_format (this->handle, hw_param, this->format);
     if (ret < 0) {
-        g_warning("could not set silence threshold: %s", snd_strerror(ret));
-        return FALSE;
+      g_warning ("Sample format (%s) not available: %s",
+                 snd_pcm_format_name (this->format), snd_strerror (ret));
+      return FALSE;
     }
-
-    ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames);
+    this->sample_bytes = snd_pcm_format_physical_width (this->format) / 8;
+  }
+
+  ret = snd_pcm_hw_params_set_channels (this->handle, hw_param, this->channels);
+  if (ret < 0) {
+    g_warning ("Channels count (%d) not available: %s", this->channels, snd_strerror (ret));
+    return FALSE;
+  }
+  this->channels = snd_pcm_hw_params_get_channels (hw_param);
+
+  if (this->rate) {
+    ret = snd_pcm_hw_params_set_rate (this->handle, hw_param, this->rate, 0);
     if (ret < 0) {
-        g_warning("could not set silence size: %s", snd_strerror(ret));
-        return FALSE;
+      g_warning ("error setting rate (%d): %s", this->rate, snd_strerror (ret));
+      return FALSE;
     }
+  }
 
-    ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames);
+  if (this->period_count) {
+    ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
     if (ret < 0) {
-        g_warning("could not set avail min: %s", snd_strerror(ret));
-        return FALSE;
+      g_warning ("error setting period count minimum (%d): %s",
+                 this->period_count, snd_strerror (ret));
+      return FALSE;
     }
+  }
 
-    ret = snd_pcm_sw_params (this->handle, sw_param);
+  if (this->period_frames) {
+    ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
     if (ret < 0) {
-        g_warning("could not set sw_params: %s", snd_strerror(ret));
-        return FALSE;
+      g_warning ("error setting period in frames (%d): %s",
+                 this->period_frames, snd_strerror (ret));
+      return FALSE;
     }
+  }
 
-    if (this->debug)
-        snd_pcm_dump(this->handle, this->out);
-
-    this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
-                                 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
-
-    if (this->access_interleaved) {
-        this->interleave_unit = this->sample_bytes;
-        this->interleave_skip = this->interleave_unit * this->channels;
-    } else {
-        this->interleave_unit = 0; /* not used */
-        this->interleave_skip = this->sample_bytes;
+  if (this->buffer_frames) {
+    ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
+    if (ret < 0) {
+      g_warning ("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror (ret));
+      return FALSE;
     }
-
-    if (this->access_addr)
-        g_free (this->access_addr);
-    this->access_addr = g_new0 (char*, this->channels);
-
-    return TRUE;
+  }
+
+  ret = snd_pcm_hw_params (this->handle, hw_param);
+  if (ret < 0) {
+    g_warning ("could not set hw params: %s", snd_strerror (ret));
+    snd_pcm_hw_params_dump (hw_param, this->out);
+    return FALSE;
+  }
+
+  if (!this->rate)
+    this->rate = snd_pcm_hw_params_get_rate (hw_param, 0);
+  if (!this->format)
+    this->format = snd_pcm_hw_params_get_format (hw_param);
+  if (!this->period_count)
+    this->period_count = snd_pcm_hw_params_get_periods (hw_param, 0);
+  if (!this->period_frames)
+    this->period_frames = snd_pcm_hw_params_get_period_size (hw_param, 0);
+  if (!this->buffer_frames)
+    this->buffer_frames = snd_pcm_hw_params_get_buffer_size (hw_param);
+  if (this->buffer_frames != this->period_count * this->period_frames)
+    g_critical ("buffer size != period size * number of periods, unexpected things may happen!");
+
+  snd_pcm_sw_params_current (this->handle, sw_param);
+
+  ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U);
+  if (ret < 0) {
+    g_warning ("could not set start mode: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
+  if (ret < 0) {
+    g_warning ("could not set stop mode: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0);
+  if (ret < 0) {
+    g_warning ("could not set silence threshold: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames);
+  if (ret < 0) {
+    g_warning ("could not set silence size: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames);
+  if (ret < 0) {
+    g_warning ("could not set avail min: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  ret = snd_pcm_sw_params (this->handle, sw_param);
+  if (ret < 0) {
+    g_warning ("could not set sw_params: %s", snd_strerror (ret));
+    return FALSE;
+  }
+
+  if (this->debug)
+    snd_pcm_dump (this->handle, this->out);
+
+  this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
+                               SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+
+  if (this->access_interleaved) {
+    this->interleave_unit = this->sample_bytes;
+    this->interleave_skip = this->interleave_unit * this->channels;
+  } else {
+    this->interleave_unit = 0;        /* not used */
+    this->interleave_skip = this->sample_bytes;
+  }
+
+  if (this->access_addr)
+    g_free (this->access_addr);
+  this->access_addr = g_new0 (char *, this->channels);
+
+  return TRUE;
 }
 
 static gboolean
-gst_alsa_open_audio(GstAlsa *this)
+gst_alsa_open_audio (GstAlsa *this)
 {
-    gint ret;
-    g_assert(this != NULL);
+  gint ret;
 
-    if (this->handle)
-        gst_alsa_close_audio(this);
+  g_assert (this != NULL);
 
-    DEBUG("Opening alsa device \"%s\" for %s...\n", this->device,
-            this->stream==SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
+  if (this->handle)
+    gst_alsa_close_audio (this);
 
-    ret = snd_output_stdio_attach(&this->out, stdout, 0);
-    if (ret < 0) {
-        g_print("error opening log output: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
+  g_print ("Opening alsa device \"%s\" for %s...\n", this->device,
+           this->stream == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
 
-    /* blocking i/o */
-    if ((ret = snd_pcm_open(&this->handle, this->device, this->stream, 0))) {
-        g_print("error opening pcm device %s: %s\n",
-               this->device, snd_strerror(ret));
-        return FALSE;
-    }
+  ret = snd_output_stdio_attach (&this->out, stdout, 0);
+  if (ret < 0) {
+    g_print ("error opening log output: %s\n", snd_strerror (ret));
+    return FALSE;
+  }
 
-    if (gst_alsa_set_params(this) == FALSE) {
-        gst_alsa_close_audio(this);
-        return FALSE;
-    }
+  /* blocking i/o */
+  if ((ret = snd_pcm_open (&this->handle, this->device, this->stream, 0))) {
+    g_print ("error opening pcm device %s: %s\n", this->device, snd_strerror (ret));
+    return FALSE;
+  }
 
-    GST_FLAG_SET(this, GST_ALSA_OPEN);
-    return TRUE;
+  if (gst_alsa_set_params (this) == FALSE) {
+    gst_alsa_close_audio (this);
+    return FALSE;
+  }
+
+  GST_FLAG_SET (this, GST_ALSA_OPEN);
+  return TRUE;
 }
 
 
 static gboolean
-gst_alsa_start_audio(GstAlsa *this)
+gst_alsa_start_audio (GstAlsa *this)
 {
-    gint err;
-    guint32 chn;
-
-    g_return_val_if_fail(this != NULL, FALSE);
-    g_return_val_if_fail(this->handle != NULL, FALSE);
+  gint err;
 
-    if ((err = snd_pcm_prepare (this->handle)) < 0) {
-        g_warning("channel prepare failed: %s", snd_strerror (err));
-        return FALSE;
-    }
+  g_return_val_if_fail (this != NULL, FALSE);
+  g_return_val_if_fail (this->handle != NULL, FALSE);
 
-    this->avail = snd_pcm_avail_update (this->handle);
+  if ((err = snd_pcm_prepare (this->handle)) < 0) {
+    g_warning ("channel prepare failed: %s", snd_strerror (err));
+    return FALSE;
+  }
 
-    if (this->stream == SND_PCM_STREAM_PLAYBACK &&
-        this->avail != this->buffer_frames) {
-        g_warning ("full buffer not available at start");
-        return FALSE;
-    }
+  this->avail = snd_pcm_avail_update (this->handle);
 
-    if (!gst_alsa_get_channel_addresses (this)) {
-        return FALSE;
-    }
+  if (this->stream == SND_PCM_STREAM_PLAYBACK && this->avail != this->buffer_frames) {
+    g_warning ("full buffer not available at start");
+    return FALSE;
+  }
 
-    if (this->stream == SND_PCM_STREAM_PLAYBACK) {
-        for (chn = 0; chn < this->channels; chn++) {
-            gst_alsa_sink_silence_on_channel (this, chn, this->buffer_frames);
-        }
-    }
+  if (!gst_alsa_get_channel_addresses (this))
+    return FALSE;
 
-    gst_alsa_release_channel_addresses (this);
+  gst_alsa_release_channel_addresses (this);
 
-    if ((err = snd_pcm_start (this->handle)) < 0) {
-        g_warning("could not start audio: %s", snd_strerror (err));
-        return FALSE;
-    }
+  if ((err = snd_pcm_start (this->handle)) < 0) {
+    g_warning ("could not start audio: %s", snd_strerror (err));
+    return FALSE;
+  }
 
-    GST_FLAG_SET(this, GST_ALSA_RUNNING);
-    return TRUE;
+  GST_FLAG_SET (this, GST_ALSA_RUNNING);
+  return TRUE;
 }
 
 static void
-gst_alsa_stop_audio(GstAlsa *this)
+gst_alsa_stop_audio (GstAlsa *this)
 {
-    gint err;
-    g_assert(this != NULL);
+  gint err;
 
-    g_return_if_fail(this != NULL);
-    g_return_if_fail(this->handle != NULL);
+  g_assert (this != NULL);
 
-    if (this->mmap_open)
-        gst_alsa_release_channel_addresses (this);
+  g_return_if_fail (this != NULL);
+  g_return_if_fail (this->handle != NULL);
 
-    if (this->stream == SND_PCM_STREAM_PLAYBACK &&
-        (err = snd_pcm_drop (this->handle)) < 0) {
-        g_warning("channel flush failed: %s", snd_strerror (err));
-        return;
-    }
+  if (this->mmap_open)
+    gst_alsa_release_channel_addresses (this);
+
+  if (this->stream == SND_PCM_STREAM_PLAYBACK && (err = snd_pcm_drop (this->handle)) < 0) {
+    g_warning ("channel flush failed: %s", snd_strerror (err));
+    return;
+  }
 
-    GST_FLAG_UNSET(this, GST_ALSA_RUNNING);
+  GST_FLAG_UNSET (this, GST_ALSA_RUNNING);
 }
 
 static void
-gst_alsa_close_audio(GstAlsa *this)
+gst_alsa_close_audio (GstAlsa *this)
 {
-    g_return_if_fail(this != NULL);
-    g_return_if_fail(this->handle != NULL);
+  g_return_if_fail (this != NULL);
+  g_return_if_fail (this->handle != NULL);
 
-    snd_pcm_close(this->handle);
+  snd_pcm_close (this->handle);
 
-    this->handle = NULL;
+  this->handle = NULL;
 
-    GST_FLAG_UNSET(this, GST_ALSA_OPEN);
+  GST_FLAG_UNSET (this, GST_ALSA_OPEN);
 }
 
 static gboolean
 gst_alsa_get_channel_addresses (GstAlsa *this)
 {
-    guint32 err, i;
-    const snd_pcm_channel_area_t *a;
-    GList *l;
+  guint32 err, i;
+  const snd_pcm_channel_area_t *a;
 
-    g_return_val_if_fail (this->mmap_open == FALSE, FALSE);
+  g_return_val_if_fail (this->mmap_open == FALSE, FALSE);
 
-    if ((err = snd_pcm_mmap_begin (this->handle, &this->mmap_areas, &this->offset, &this->avail)) < 0) {
-        g_warning("gstalsa: mmap failed: %s", snd_strerror(err));
-        return FALSE;
-    }
+  if ((err = snd_pcm_mmap_begin (this->handle, &this->mmap_areas, &this->offset, &this->avail)) < 0) {
+    g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
+    return FALSE;
+  }
 
-    GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail);
+  GST_DEBUG (0, "got %d mmap'd frames", (int) this->avail);
 
-    l = this->pads;
-    while (l) {
-        a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ?
-                             GST_ALSA_PAD(l)->channel-1 : 0];
-        GST_ALSA_PAD(l)->access_addr = (char *) a->addr + ((a->first + a->step *
-                                                            this->offset) / 8);
-        l = l->next;
-    }
+  for (i = 0; i < this->channels; i++) {
+    a = &this->mmap_areas[i];
+    this->access_addr[i] =
+      (char *) a->addr + ((a->first + a->step * this->offset) / 8);
+  }
 
-    for (i=0; i<this->channels; i++) {
-        a = &this->mmap_areas[i];
-        this->access_addr[i] = (char *) a->addr + ((a->first + a->step *
-                                                    this->offset) / 8);
-    }
-
-    this->mmap_open = TRUE;
+  this->mmap_open = TRUE;
 
-    return TRUE;
+  return TRUE;
 }
 
 static void
-gst_alsa_release_channel_addresses (GstAlsa *this)
+gst_alsa_release_channel_addresses (GstAlsa * this)
 {
-    guint32 err, i;
-    GList *l;
+  guint32 err, i;
 
-    g_return_if_fail (this->mmap_open == TRUE);
+  g_return_if_fail (this->mmap_open == TRUE);
 
-    GST_DEBUG(0, "releasing mmap'd data region: %d frames", (int)this->avail);
+  GST_DEBUG (0, "releasing mmap'd data region: %d frames", (int) this->avail);
 
-    if ((err = snd_pcm_mmap_commit (this->handle, this->offset, this->avail)) < 0) {
-        g_warning("gstalsa: mmap commit failed: %s", snd_strerror(err));
-        return;
-    }
+  if ((err = snd_pcm_mmap_commit (this->handle, this->offset, this->avail)) < 0) {
+    g_warning ("gstalsa: mmap commit failed: %s", snd_strerror (err));
+    return;
+  }
 
-    l = this->pads;
-    while (l) {
-        GST_ALSA_PAD(l)->access_addr = NULL;
-        l = l->next;
-    }
-
-    for (i=0; i<this->channels; i++) {
-        this->access_addr[i] = NULL;
-    }
+  for (i = 0; i < this->channels; i++)
+    this->access_addr[i] = NULL;
 
-    this->mmap_open = FALSE;
-    this->avail=0;
+  this->mmap_open = FALSE;
+  this->avail = 0;
 }
 
-static void
-gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes)
-{
-    if (this->access_interleaved) {
-        memset_interleave
-            (this->access_addr[chn],
-             0, nframes * this->sample_bytes,
-             this->interleave_unit,
-             this->interleave_skip);
-    } else {
-        memset (this->access_addr[chn], 0, nframes * this->sample_bytes);
-    }
-}
-
-/* taken directly from paul davis' memops.cc */
-static void
-memset_interleave (char *dst, char val, unsigned int bytes,
-                   unsigned int unit_bytes,
-                   unsigned int skip_bytes)
-{
-    switch (unit_bytes) {
-    case 1:
-        while (bytes--) {
-            *dst = val;
-            dst += skip_bytes;
-        }
-        break;
-    case 2:
-        while (bytes) {
-            *((short *) dst) = (short) val;
-            dst += skip_bytes;
-            bytes -= 2;
-        }
-        break;
-    case 4:
-        while (bytes) {
-            *((int *) dst) = (int) val;
-            dst += skip_bytes;
-            bytes -= 4;
-        }
-        break;
-    }
-}
+/*** GSTREAMER PLUGIN *********************************************************/
 
 static gboolean
-plugin_init (GModule *module, GstPlugin *plugin)
+plugin_init (GModule * module, GstPlugin * plugin)
 {
-    GstElementFactory *factory;
+  GstElementFactory *factory;
 
-    if (!gst_library_load ("gstbytestream"))
-        return FALSE;
+  if (!gst_library_load ("gstbytestream"))
+    return FALSE;
+
+  factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC, &gst_alsa_src_details);
+  g_return_val_if_fail (factory != NULL, FALSE);
+  gst_element_factory_add_pad_template (factory, gst_alsa_src_pad_factory ());
+  gst_element_factory_add_pad_template (factory, gst_alsa_src_request_pad_factory ());
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
 
-    factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC,
-                                      &gst_alsa_src_details);
-    g_return_val_if_fail (factory != NULL, FALSE);
-    gst_element_factory_add_pad_template (factory, gst_alsa_src_pad_factory ());
-    gst_element_factory_add_pad_template (factory,
-                                         gst_alsa_src_request_pad_factory ());
-    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-
-    factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK,
-                                      &gst_alsa_sink_details);
-    g_return_val_if_fail (factory != NULL, FALSE);
-    gst_element_factory_add_pad_template (factory, gst_alsa_sink_pad_factory());
-    gst_element_factory_add_pad_template (factory,
-                                         gst_alsa_sink_request_pad_factory());
-    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-
-    gst_plugin_set_longname(plugin, "ALSA plugin library");
-
-    return TRUE;
+  factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK, &gst_alsa_sink_details);
+  g_return_val_if_fail (factory != NULL, FALSE);
+  gst_element_factory_add_pad_template (factory, gst_alsa_sink_pad_factory ());
+  gst_element_factory_add_pad_template (factory, gst_alsa_sink_request_pad_factory ());
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+  gst_plugin_set_longname (plugin, "ALSA plugin library");
+
+  return TRUE;
 }
 
 GstPluginDesc plugin_desc = {
-    GST_VERSION_MAJOR,
-    GST_VERSION_MINOR,
-    "alsa",
-    plugin_init
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "alsa",
+  plugin_init
 };
-
-
index 56de5e125c35268c5915be619d212b27eaaf9f0d..d8ef53180469dcf5245e317692bdcf5b3d94720b 100644 (file)
@@ -1,23 +1,22 @@
-/* -*- c-basic-offset: 4 -*- */
 /*
   Copyright (C) 2001 CodeFactory AB
   Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
   Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
-                            
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
* Copyright (C) 2001 CodeFactory AB
* Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
* Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
+ *
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 
 #ifndef __GST_ALSA_H__
 #define __GST_ALSA_H__
@@ -58,68 +57,68 @@ typedef GstAlsa GstAlsaSrc;
 typedef GstAlsaClass GstAlsaSrcClass;
 
 enum {
-    GST_ALSA_OPEN = GST_ELEMENT_FLAG_LAST,
-    GST_ALSA_RUNNING,
-    GST_ALSA_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 3,
+  GST_ALSA_OPEN = GST_ELEMENT_FLAG_LAST,
+  GST_ALSA_RUNNING,
+  GST_ALSA_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 3,
 };
 
 typedef gboolean (*GstAlsaProcessFunc) (GstAlsa *, snd_pcm_uframes_t frames);
 
 typedef struct {
-    gint channel;
-    GstPad *pad;
-    GstByteStream *bs;
-    /* pointer to where we can access mmap_areas */
-    char *access_addr;
-    char *buf;
-    /* how much of the buffer we have used */
-    snd_pcm_uframes_t offset;
+  gint channel;
+  GstPad *pad;
+  GstByteStream *bs;
+
+  /* buf and offset are only used for src elements to hold data from the sound
+     card, while we're waiting for an entire period_frames data. */
+  char *buf;
+  snd_pcm_uframes_t offset;
 } GstAlsaPad;
 
 struct _GstAlsa {
-    GstElement parent;
-
-    /* list of GstAlsaPads */
-    GList *pads;
-    
-    gchar *device;
-    snd_pcm_stream_t stream;
-    
-    snd_pcm_t *handle;
-    snd_output_t *out;
-    
-    /* our mmap'd data areas */
-    gboolean mmap_open;
-    const snd_pcm_channel_area_t *mmap_areas;
-    char **access_addr;
-    snd_pcm_uframes_t offset;
-    snd_pcm_sframes_t avail;
-    
-    GstAlsaProcessFunc process;
-    
-    snd_pcm_format_t format;
-    guint rate;
-    gint channels;
-    guint32 mute; /* bitmask. */
-    
-    /* the gstreamer data */
-    gboolean data_interleaved;
-    
-    /* access to the hardware */
-    gboolean access_interleaved;
-    guint sample_bytes;
-    guint interleave_unit;
-    guint interleave_skip;
-    
-    guint buffer_frames;
-    guint period_count; /* 'number of fragments' in oss-speak */
-    guint period_frames;
-
-    gboolean debug;
+  GstElement parent;
+
+  /* list of GstAlsaPads */
+  GList *pads;
+
+  gchar *device;
+  snd_pcm_stream_t stream;
+  snd_pcm_t *handle;
+  snd_output_t *out;
+
+  /* our mmap'd data areas */
+  gboolean mmap_open;
+  const snd_pcm_channel_area_t *mmap_areas;
+  char **access_addr;
+  snd_pcm_uframes_t offset;
+  snd_pcm_sframes_t avail;
+
+  GstAlsaProcessFunc process;
+
+  snd_pcm_format_t format;
+  guint rate;
+  gint channels;
+  guint32 mute; /* bitmask. */
+
+  /* the gstreamer data */
+  gboolean data_interleaved;
+  gboolean autorecover;
+
+  /* access to the hardware */
+  gboolean access_interleaved;
+  guint sample_bytes;
+  guint interleave_unit;
+  guint interleave_skip;
+
+  guint buffer_frames;
+  guint period_count; /* 'number of fragments' in oss-speak */
+  guint period_frames;
+
+  gboolean debug;
 };
 
 struct _GstAlsaClass {
-    GstElementClass parent_class;
+  GstElementClass parent_class;
 };
 
 GType gst_alsa_get_type (void);