Added audiosource base classes.
authorWim Taymans <wim.taymans@gmail.com>
Wed, 6 Jul 2005 15:27:17 +0000 (15:27 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 6 Jul 2005 15:27:17 +0000 (15:27 +0000)
Original commit message from CVS:
Added audiosource base classes.
Ported alsasrc, still very basic.

14 files changed:
ChangeLog
ext/alsa/Makefile.am
ext/alsa/gstalsaplugin.c
ext/alsa/gstalsasrc.c
ext/alsa/gstalsasrc.h
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/gstaudiosink.c
gst-libs/gst/audio/gstaudiosrc.c [new file with mode: 0644]
gst-libs/gst/audio/gstaudiosrc.h [new file with mode: 0644]
gst-libs/gst/audio/gstbaseaudiosink.c
gst-libs/gst/audio/gstbaseaudiosrc.c [new file with mode: 0644]
gst-libs/gst/audio/gstbaseaudiosrc.h [new file with mode: 0644]
gst-libs/gst/audio/gstringbuffer.c
gst-libs/gst/audio/gstringbuffer.h

index 7bad610..74d2655 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,56 @@
 2005-07-06  Wim Taymans  <wim@fluendo.com>
 
+       * ext/alsa/Makefile.am:
+       * ext/alsa/gstalsaplugin.c: (plugin_init):
+       * ext/alsa/gstalsasink.c: (gst_alsasink_open):
+       * ext/alsa/gstalsasrc.c: (gst_alsasrc_get_type),
+       (gst_alsasrc_dispose), (gst_alsasrc_base_init),
+       (gst_alsasrc_class_init), (gst_alsasrc_init),
+       (gst_alsasrc_getcaps), (set_hwparams), (set_swparams),
+       (alsasrc_parse_spec), (gst_alsasrc_open), (gst_alsasrc_close),
+       (xrun_recovery), (gst_alsasrc_read), (gst_alsasrc_delay),
+       (gst_alsasrc_reset):
+       * ext/alsa/gstalsasrc.h:
+       * gst-libs/gst/audio/Makefile.am:
+       * gst-libs/gst/audio/gstaudiosink.c:
+       (gst_audioringbuffer_get_type), (gst_audioringbuffer_class_init),
+       (gst_audioringbuffer_start):
+       * gst-libs/gst/audio/gstaudiosrc.c: (gst_audioringbuffer_get_type),
+       (gst_audioringbuffer_class_init), (audioringbuffer_thread_func),
+       (gst_audioringbuffer_init), (gst_audioringbuffer_dispose),
+       (gst_audioringbuffer_finalize), (gst_audioringbuffer_acquire),
+       (gst_audioringbuffer_release), (gst_audioringbuffer_start),
+       (gst_audioringbuffer_stop), (gst_audioringbuffer_delay),
+       (gst_audiosrc_base_init), (gst_audiosrc_class_init),
+       (gst_audiosrc_init), (gst_audiosrc_create_ringbuffer):
+       * gst-libs/gst/audio/gstaudiosrc.h:
+       * gst-libs/gst/audio/gstbaseaudiosink.c:
+       (gst_baseaudiosink_class_init), (gst_baseaudiosink_dispose),
+       (gst_baseaudiosink_get_time), (gst_baseaudiosink_setcaps),
+       (gst_baseaudiosink_preroll), (gst_baseaudiosink_render):
+       * gst-libs/gst/audio/gstbaseaudiosrc.c:
+       (gst_baseaudiosrc_base_init), (gst_baseaudiosrc_class_init),
+       (gst_baseaudiosrc_init), (gst_baseaudiosrc_get_clock),
+       (gst_baseaudiosrc_get_time), (gst_baseaudiosrc_set_property),
+       (gst_baseaudiosrc_get_property), (gst_baseaudiosrc_fixate),
+       (gst_baseaudiosrc_setcaps), (gst_baseaudiosrc_get_times),
+       (gst_baseaudiosrc_event), (gst_baseaudiosrc_create),
+       (gst_baseaudiosrc_create_ringbuffer), (gst_baseaudiosrc_callback),
+       (gst_baseaudiosrc_change_state):
+       * gst-libs/gst/audio/gstbaseaudiosrc.h:
+       * gst-libs/gst/audio/gstringbuffer.c: (build_linear_format),
+       (gst_ringbuffer_debug_spec_caps), (gst_ringbuffer_debug_spec_buff),
+       (gst_ringbuffer_parse_caps), (gst_ringbuffer_start),
+       (gst_ringbuffer_pause), (gst_ringbuffer_stop),
+       (gst_ringbuffer_samples_done), (gst_ringbuffer_set_sample),
+       (wait_segment), (gst_ringbuffer_commit), (gst_ringbuffer_read),
+       (gst_ringbuffer_prepare_read), (gst_ringbuffer_advance):
+       * gst-libs/gst/audio/gstringbuffer.h:
+       Added audiosource base classes.
+       Ported alsasrc, still very basic.
+
+2005-07-06  Wim Taymans  <wim@fluendo.com>
+
        * ext/theora/theoradec.c: (theora_dec_src_getcaps),
        (theora_dec_push), (theora_handle_data_packet):
        Prepare for better timestamp fix later.
index 0a02686..d976b00 100644 (file)
@@ -3,7 +3,8 @@ plugin_LTLIBRARIES = libgstalsa.la
 
 libgstalsa_la_SOURCES = \
        gstalsaplugin.c \
-       gstalsasink.c 
+       gstalsasink.c   \
+       gstalsasrc.c
 
 # port alsa stuff then add the _SOURCES above
 EXTRA_DIST = \
@@ -11,8 +12,7 @@ EXTRA_DIST = \
        gstalsamixertrack.c \
        gstalsamixeroptions.c \
        gstalsa.c \
-       gstalsaclock.c \
-       gstalsasrc.c
+       gstalsaclock.c 
 
 libgstalsa_la_CFLAGS = $(GST_CFLAGS) $(ALSA_CFLAGS)
 libgstalsa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \
index dfa0e5b..5fa547c 100644 (file)
@@ -60,10 +60,10 @@ plugin_init (GstPlugin * plugin)
      if (!gst_element_register (plugin, "alsamixer", GST_RANK_NONE,
      GST_TYPE_ALSA_MIXER))
      return FALSE;
-     if (!gst_element_register (plugin, "alsasrc", GST_RANK_NONE,
-     GST_TYPE_ALSA_SRC))
-     return FALSE;
    */
+  if (!gst_element_register (plugin, "alsasrc", GST_RANK_NONE,
+          GST_TYPE_ALSA_SRC))
+    return FALSE;
   if (!gst_element_register (plugin, "alsasink", GST_RANK_NONE,
           GST_TYPE_ALSA_SINK))
     return FALSE;
index 309f4eb..c1ed44a 100644 (file)
@@ -1,8 +1,7 @@
-/*
- * Copyright (C) 2001 CodeFactory AB
- * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
- * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstalsasrc.c: 
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  *
  * 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
+ * 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.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <alsa/asoundlib.h>
+
 
 #include "gstalsasrc.h"
-#include "gstalsaclock.h"
 
 /* elementfactory information */
-static GstElementDetails gst_alsa_src_details = GST_ELEMENT_DETAILS ("Alsa Src",
-    "Source/Audio",
-    "Read from a sound card via ALSA",
-    "Thomas Nyberg <thomas@codefactory.se>, "
-    "Andy Wingo <apwingo@eos.ncsu.edu>, "
-    "Benjamin Otte <in7y118@public.uni-hamburg.de>");
-
-static GstPadTemplate *gst_alsa_src_pad_factory (void);
-static void gst_alsa_src_base_init (gpointer g_class);
-static void gst_alsa_src_class_init (gpointer g_class, gpointer class_data);
-static void gst_alsa_src_init (GstAlsaSrc * this);
-static int gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail);
-static int gst_alsa_src_read (GstAlsa * this, snd_pcm_sframes_t * avail);
-static void gst_alsa_src_loop (GstElement * element);
-static void gst_alsa_src_flush (GstAlsaSrc * src);
-static GstElementStateReturn gst_alsa_src_change_state (GstElement * element);
-static GstClockTime gst_alsa_src_get_time (GstAlsa * this);
-
-static GstAlsa *src_parent_class = NULL;
-
-static GstPadTemplate *
-gst_alsa_src_pad_factory (void)
+static GstElementDetails gst_alsasrc_details =
+GST_ELEMENT_DETAILS ("Audio Src (ALSA)",
+    "Src/Audio",
+    "Output to a sound card via ALSA",
+    "Wim Taymans <wim@fluendo.com>");
+
+static void gst_alsasrc_base_init (gpointer g_class);
+static void gst_alsasrc_class_init (GstAlsaSrcClass * klass);
+static void gst_alsasrc_init (GstAlsaSrc * alsasrc);
+static void gst_alsasrc_dispose (GObject * object);
+
+static GstCaps *gst_alsasrc_getcaps (GstBaseSrc * bsrc);
+
+static gboolean gst_alsasrc_open (GstAudioSrc * asrc, GstRingBufferSpec * spec);
+static gboolean gst_alsasrc_close (GstAudioSrc * asrc);
+static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length);
+static guint gst_alsasrc_delay (GstAudioSrc * asrc);
+static void gst_alsasrc_reset (GstAudioSrc * asrc);
+
+/* AlsaSrc signals and args */
+enum
 {
-  static GstPadTemplate *template = NULL;
-
-  if (!template)
-    template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
-        gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, -1));
-
-  return template;
-}
+  LAST_SIGNAL
+};
+
+static GstStaticPadTemplate alsasrc_src_factory =
+    GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
+        "signed = (boolean) { TRUE, FALSE }, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
+        "audio/x-raw-int, "
+        "signed = (boolean) { TRUE, FALSE }, "
+        "width = (int) 8, "
+        "depth = (int) 8, "
+        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
+    );
+
+static GstElementClass *parent_class = NULL;
+
+/* static guint gst_alsasrc_signals[LAST_SIGNAL] = { 0 }; */
 
 GType
-gst_alsa_src_get_type (void)
+gst_alsasrc_get_type (void)
 {
-  static GType alsa_src_type = 0;
+  static GType alsasrc_type = 0;
 
-  if (!alsa_src_type) {
-    static const GTypeInfo alsa_src_info = {
+  if (!alsasrc_type) {
+    static const GTypeInfo alsasrc_info = {
       sizeof (GstAlsaSrcClass),
-      gst_alsa_src_base_init,
+      gst_alsasrc_base_init,
       NULL,
-      gst_alsa_src_class_init,
+      (GClassInitFunc) gst_alsasrc_class_init,
       NULL,
       NULL,
       sizeof (GstAlsaSrc),
       0,
-      (GInstanceInitFunc) gst_alsa_src_init,
+      (GInstanceInitFunc) gst_alsasrc_init,
     };
 
-    alsa_src_type =
-        g_type_register_static (GST_TYPE_ALSA_MIXER, "GstAlsaSrc",
-        &alsa_src_info, 0);
+    alsasrc_type =
+        g_type_register_static (GST_TYPE_AUDIOSRC, "GstAlsaSrc",
+        &alsasrc_info, 0);
   }
-  return alsa_src_type;
+
+  return alsasrc_type;
+}
+
+static void
+gst_alsasrc_dispose (GObject * object)
+{
+  G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
-gst_alsa_src_base_init (gpointer g_class)
+gst_alsasrc_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_add_pad_template (element_class,
-      gst_alsa_src_pad_factory ());
+  gst_element_class_set_details (element_class, &gst_alsasrc_details);
 
-  gst_element_class_set_details (element_class, &gst_alsa_src_details);
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&alsasrc_src_factory));
 }
-
 static void
-gst_alsa_src_class_init (gpointer g_class, gpointer class_data)
+gst_alsasrc_class_init (GstAlsaSrcClass * klass)
 {
-  GObjectClass *object_class;
-  GstElementClass *element_class;
-  GstAlsaClass *alsa_class;
-  GstAlsaSrcClass *klass;
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstBaseAudioSrcClass *gstbaseaudiosrc_class;
+  GstAudioSrcClass *gstaudiosrc_class;
 
-  klass = (GstAlsaSrcClass *) g_class;
-  object_class = (GObjectClass *) klass;
-  element_class = (GstElementClass *) klass;
-  alsa_class = (GstAlsaClass *) klass;
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+  gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
+  gstaudiosrc_class = (GstAudioSrcClass *) klass;
 
-  if (src_parent_class == NULL)
-    src_parent_class = g_type_class_ref (GST_TYPE_ALSA_MIXER);
+  parent_class = g_type_class_ref (GST_TYPE_BASEAUDIOSRC);
 
-  alsa_class->stream = SND_PCM_STREAM_CAPTURE;
-  alsa_class->transmit_mmap = gst_alsa_src_mmap;
-  alsa_class->transmit_rw = gst_alsa_src_read;
+  gobject_class->dispose = gst_alsasrc_dispose;
 
-  element_class->change_state = gst_alsa_src_change_state;
+  gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps);
+
+  gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open);
+  gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_alsasrc_close);
+  gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_alsasrc_read);
+  gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_alsasrc_delay);
+  gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_alsasrc_reset);
 }
 
 static void
-gst_alsa_src_init (GstAlsaSrc * src)
+gst_alsasrc_init (GstAlsaSrc * alsasrc)
 {
-  GstAlsa *this = GST_ALSA (src);
-
-  this->pad[0] = gst_pad_new_from_template (gst_alsa_src_pad_factory (), "src");
-  gst_pad_set_link_function (this->pad[0], gst_alsa_link);
-  gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps);
-  gst_element_add_pad (GST_ELEMENT (this), this->pad[0]);
+  GST_DEBUG ("initializing alsasrc");
 
-  this->clock =
-      gst_alsa_clock_new ("alsasrcclock", gst_alsa_src_get_time, this);
-  /* we hold a ref to our clock until we're disposed */
-  gst_object_ref (this->clock);
-  gst_object_sink (GST_OBJECT (this->clock));
-
-  src->status = NULL;
-  gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop);
+  alsasrc->device = g_strdup ("default");
 }
 
-/* alsasrc provides a clock starting from the trigger tstamp
- * (last play/pause/stop), and added to that the time for the currently
- * processed samples, and the current fill state of the buffer */
-static GstClockTime
-gst_alsa_src_get_time (GstAlsa * this)
+static GstCaps *
+gst_alsasrc_getcaps (GstBaseSrc * bsrc)
 {
-  struct timeval trigger;
-  snd_pcm_sframes_t delay;
-  GstClockTime gct_trigger, gct_captured, gct_delay, retval =
-      GST_CLOCK_TIME_NONE;
-  int err;
-  GstAlsaSrc *src = GST_ALSA_SRC (this);
-
-  GTimeVal now;
-
-  g_get_current_time (&now);
-  return GST_TIMEVAL_TO_TIME (now);
+  return NULL;
+}
 
-  if (src->status == NULL)
-    return GST_CLOCK_TIME_NONE;
+#define CHECK(call, error) \
+G_STMT_START {                 \
+if ((err = call) < 0)          \
+  goto error;                  \
+} G_STMT_END;
 
-  if ((err = snd_pcm_status (this->handle, src->status)) < 0) {
-    GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err));
-    return GST_CLOCK_TIME_NONE;
+static int
+set_hwparams (GstAlsaSrc * alsa)
+{
+  guint rrate;
+  gint err, dir;
+  snd_pcm_hw_params_t *params;
+
+  snd_pcm_hw_params_alloca (&params);
+
+  /* choose all parameters */
+  CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config);
+  /* set the interleaved read/write format */
+  CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access),
+      wrong_access);
+  /* set the sample format */
+  CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format),
+      no_sample_format);
+  /* set the count of channels */
+  CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels),
+      no_channels);
+  /* set the stream rate */
+  rrate = alsa->rate;
+  CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, 0),
+      no_rate);
+  if (rrate != alsa->rate)
+    goto rate_match;
+
+  if (alsa->buffer_time != -1) {
+    /* set the buffer time */
+    CHECK (snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params,
+            &alsa->buffer_time, &dir), buffer_time);
+  }
+  if (alsa->period_time != -1) {
+    /* set the period time */
+    CHECK (snd_pcm_hw_params_set_period_time_near (alsa->handle, params,
+            &alsa->period_time, &dir), period_time);
   }
 
-  /* trigger tstamp is the last time the device got started/stopped/paused */
-  snd_pcm_status_get_trigger_tstamp (src->status, &trigger);
-  gct_trigger = GST_TIMEVAL_TO_TIME (trigger);
+  /* write the parameters to device */
+  CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);
 
-  /* captured is the number of samples already sent out as buffers */
-  gct_captured = gst_alsa_samples_to_timestamp (this, this->captured);
+  CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size),
+      buffer_size);
 
-  /* delay is the number of samples in the buffer not yet processed */
-  delay = snd_pcm_status_get_delay (src->status);
-  gct_delay = gst_alsa_samples_to_timestamp (this, delay);
+  CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir),
+      period_size);
 
-  retval = gct_trigger + gct_captured + gct_delay;
-  GST_LOG_OBJECT (src, "returning clock time of %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (retval));
-  return retval;
-}
+  return 0;
 
-static int
-gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
-{
-  snd_pcm_uframes_t offset;
-  snd_pcm_channel_area_t *dst;
-  const snd_pcm_channel_area_t *src;
-  int i, err, width = snd_pcm_format_physical_width (this->format->format);
-  GstAlsaSrc *alsa_src = GST_ALSA_SRC (this);
-
-  /* areas points to the memory areas that belong to gstreamer. */
-  dst = g_malloc0 (this->format->channels * sizeof (snd_pcm_channel_area_t));
-
-  if (((GstElement *) this)->numpads == 1) {
-    /* interleaved */
-    for (i = 0; i < this->format->channels; i++) {
-      dst[i].addr = alsa_src->buf[0]->data;
-      dst[i].first = i * width;
-      dst[i].step = this->format->channels * width;
-    }
-  } else {
-    /* noninterleaved */
-    for (i = 0; i < this->format->channels; i++) {
-      dst[i].addr = alsa_src->buf[i]->data;
-      dst[i].first = 0;
-      dst[i].step = width;
-    }
+  /* ERRORS */
+no_config:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Broken configuration for recording: no configurations available: %s",
+            snd_strerror (err)), (NULL));
+    return err;
   }
-
-  if ((err = snd_pcm_mmap_begin (this->handle, &src, &offset, avail)) < 0) {
-    GST_ERROR_OBJECT (this, "mmap failed: %s", snd_strerror (err));
-    return -1;
+wrong_access:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Access type not available for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
+  }
+no_sample_format:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Sample format not available for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
   }
-  if (*avail > 0
-      && (err =
-          snd_pcm_areas_copy (dst, 0, src, offset, this->format->channels,
-              *avail, this->format->format)) < 0) {
-    snd_pcm_mmap_commit (this->handle, offset, 0);
-    GST_ERROR_OBJECT (this, "data copy failed: %s", snd_strerror (err));
-    return -1;
+no_channels:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Channels count (%i) not available for recording: %s",
+            alsa->channels, snd_strerror (err)), (NULL));
+    return err;
   }
-  if ((err = snd_pcm_mmap_commit (this->handle, offset, *avail)) < 0) {
-    GST_ERROR_OBJECT (this, "mmap commit failed: %s", snd_strerror (err));
-    return -1;
+no_rate:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Rate %iHz not available for recording: %s",
+            alsa->rate, snd_strerror (err)), (NULL));
+    return err;
+  }
+rate_match:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Rate doesn't match (requested %iHz, get %iHz)",
+            alsa->rate, err), (NULL));
+    return -EINVAL;
+  }
+buffer_time:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set buffer time %i for recording: %s",
+            alsa->buffer_time, snd_strerror (err)), (NULL));
+    return err;
+  }
+buffer_size:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to get buffer size for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
+  }
+period_time:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set period time %i for recording: %s", alsa->period_time,
+            snd_strerror (err)), (NULL));
+    return err;
+  }
+period_size:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to get period size for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
+  }
+set_hw_params:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set hw params for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
   }
-
-  return err;
 }
+
 static int
-gst_alsa_src_read (GstAlsa * this, snd_pcm_sframes_t * avail)
+set_swparams (GstAlsaSrc * alsa)
 {
-  void *channels[this->format->channels];
-  int err, i;
-  GstAlsaSrc *src = GST_ALSA_SRC (this);
-
-  if (((GstElement *) this)->numpads == 1) {
-    /* interleaved */
-    err = snd_pcm_readi (this->handle, src->buf[0]->data, *avail);
-  } else {
-    /* noninterleaved */
-    for (i = 0; i < this->format->channels; i++) {
-      channels[i] = src->buf[i]->data;
-    }
-    err = snd_pcm_readn (this->handle, channels, *avail);
+  int err;
+  snd_pcm_sw_params_t *params;
+
+  snd_pcm_sw_params_alloca (&params);
+
+  /* get the current swparams */
+  CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config);
+  /* start the transfer when the buffer is almost full: */
+  /* (buffer_size / avail_min) * avail_min */
+#if 0
+  CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params,
+          (alsa->buffer_size / alsa->period_size) * alsa->period_size),
+      start_threshold);
+
+  /* allow the transfer when at least period_size samples can be processed */
+  CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params,
+          alsa->period_size), set_avail);
+#endif
+  /* align all transfers to 1 sample */
+  CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align);
+
+  /* write the parameters to the recording device */
+  CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params);
+
+  return 0;
+
+  /* ERRORS */
+no_config:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to determine current swparams for recording: %s",
+            snd_strerror (err)), (NULL));
+    return err;
   }
-  /* error handling */
-  if (err < 0) {
-    if (err == -EPIPE) {
-      gst_alsa_xrun_recovery (this);
-      return 0;
-    }
-    GST_ERROR_OBJECT (this, "error on data access: %s", snd_strerror (err));
+#if 0
+start_threshold:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set start threshold mode for recording: %s",
+            snd_strerror (err)), (NULL));
+    return err;
+  }
+set_avail:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set avail min for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
+  }
+#endif
+set_align:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set transfer align for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
+  }
+set_sw_params:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Unable to set sw params for recording: %s", snd_strerror (err)),
+        (NULL));
+    return err;
   }
-  return err;
 }
-static inline gint
-gst_alsa_src_adjust_rate (gint rate, gboolean aggressive)
-{
-  static gint rates[] = { 96000, 48000, 44100, 22050, 8000 };
-  gint i;
 
-  if (aggressive)
-    return rate;
+static gboolean
+alsasrc_parse_spec (GstAlsaSrc * alsa, GstRingBufferSpec * spec)
+{
+  switch (spec->type) {
+    case GST_BUFTYPE_LINEAR:
+      alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width,
+          spec->sign ? 0 : 1, spec->bigend ? 1 : 0);
+      break;
+    case GST_BUFTYPE_FLOAT:
+      switch (spec->format) {
+        case GST_FLOAT32_LE:
+          alsa->format = SND_PCM_FORMAT_FLOAT_LE;
+          break;
+        case GST_FLOAT32_BE:
+          alsa->format = SND_PCM_FORMAT_FLOAT_BE;
+          break;
+        case GST_FLOAT64_LE:
+          alsa->format = SND_PCM_FORMAT_FLOAT64_LE;
+          break;
+        case GST_FLOAT64_BE:
+          alsa->format = SND_PCM_FORMAT_FLOAT64_BE;
+          break;
+        default:
+          goto error;
+      }
+      break;
+    case GST_BUFTYPE_A_LAW:
+      alsa->format = SND_PCM_FORMAT_A_LAW;
+      break;
+    case GST_BUFTYPE_MU_LAW:
+      alsa->format = SND_PCM_FORMAT_MU_LAW;
+      break;
+    default:
+      goto error;
 
-  for (i = 0; i < G_N_ELEMENTS (rates); i++) {
-    if (rate >= rates[i])
-      return rates[i];
   }
+  alsa->rate = spec->rate;
+  alsa->channels = spec->channels;
+  alsa->buffer_time = spec->buffer_time;
+  alsa->period_time = spec->latency_time;
+  alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED;
 
-  return 0;
+  return TRUE;
+
+  /* ERRORS */
+error:
+  {
+    return FALSE;
+  }
 }
 
 static gboolean
-gst_alsa_src_set_caps (GstAlsaSrc * src, gboolean aggressive)
+gst_alsasrc_open (GstAudioSrc * asrc, GstRingBufferSpec * spec)
 {
-  GstCaps *all_caps, *caps;
-  GstStructure *structure, *walk;
-  gint channels, min_channels, max_channels;
-  gint rate, min_rate, max_rate;
-  gint i, endian, width, depth;
-  gboolean sign;
-  GstAlsa *this = GST_ALSA (src);
-
-  all_caps = gst_alsa_get_caps (this->pad[0]);
-  if (all_caps == NULL)
+  GstAlsaSrc *alsa;
+  gint err;
+
+  alsa = GST_ALSA_SRC (asrc);
+
+  if (!alsasrc_parse_spec (alsa, spec))
+    goto spec_parse;
+
+  CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_CAPTURE,
+          SND_PCM_NONBLOCK), open_error);
+
+  CHECK (snd_pcm_nonblock (alsa->handle, 0), non_block);
+
+  CHECK (set_hwparams (alsa), hw_params_failed);
+  CHECK (set_swparams (alsa), sw_params_failed);
+  CHECK (snd_pcm_prepare (alsa->handle), prepare_failed);
+
+  alsa->bytes_per_sample = spec->bytes_per_sample;
+  spec->segsize = alsa->period_size * spec->bytes_per_sample;
+  spec->segtotal = alsa->buffer_size / alsa->period_size;
+  spec->silence_sample[0] = 0;
+  spec->silence_sample[1] = 0;
+  spec->silence_sample[2] = 0;
+  spec->silence_sample[3] = 0;
+
+  return TRUE;
+
+  /* ERRORS */
+spec_parse:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Error parsing spec"), (NULL));
+    return FALSE;
+  }
+open_error:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Recording open error: %s", snd_strerror (err)), (NULL));
     return FALSE;
-  /* now intersect this with all caps of the peers... */
-  for (i = 0; i < GST_ELEMENT (src)->numpads; i++) {
-    all_caps =
-        gst_caps_intersect (all_caps, gst_pad_get_allowed_caps (this->pad[i]));
-    if (all_caps == NULL) {
-      GST_DEBUG ("No compatible caps found in alsasrc (%s)",
-          GST_ELEMENT_NAME (this));
-      return FALSE;
-    }
   }
+non_block:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Could not set device to blocking: %s", snd_strerror (err)), (NULL));
+    return FALSE;
+  }
+hw_params_failed:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Setting of hwparams failed: %s", snd_strerror (err)), (NULL));
+    return FALSE;
+  }
+sw_params_failed:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Setting of swparams failed: %s", snd_strerror (err)), (NULL));
+    return FALSE;
+  }
+prepare_failed:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("Prepare failed: %s", snd_strerror (err)), (NULL));
+    return FALSE;
+  }
+}
 
-  /* construct caps */
-  caps = gst_caps_new_simple ("audio/x-raw-int", NULL);
-  g_assert (gst_caps_get_size (caps) == 1);
-  structure = gst_caps_get_structure (caps, 0);
+static gboolean
+gst_alsasrc_close (GstAudioSrc * asrc)
+{
+  GstAlsaSrc *alsa;
 
-  /* now try to find the best match */
-  for (i = 0; i < gst_caps_get_size (all_caps); i++) {
-    walk = gst_caps_get_structure (all_caps, i);
-    if (!(gst_structure_get_int (walk, "signed", &sign) &&
-            gst_structure_get_int (walk, "width", &width) &&
-            gst_structure_get_int (walk, "depth", &depth))) {
-      GST_ERROR_OBJECT (src, "couldn't parse my own format. Huh?");
-      continue;
-    }
-    if (!gst_structure_get_int (walk, "endianness", &endian)) {
-      endian = G_BYTE_ORDER;
-    }
-    gst_structure_set (structure,
-        "endianness", G_TYPE_INT, endian,
-        "width", G_TYPE_INT, width,
-        "depth", G_TYPE_INT, depth, "signed", G_TYPE_BOOLEAN, sign, NULL);
-
-    min_rate =
-        gst_value_get_int_range_min (gst_structure_get_value (walk, "rate"));
-    max_rate =
-        gst_value_get_int_range_max (gst_structure_get_value (walk, "rate"));
-    min_channels =
-        gst_value_get_int_range_min (gst_structure_get_value (walk,
-            "channels"));
-    max_channels =
-        gst_value_get_int_range_max (gst_structure_get_value (walk,
-            "channels"));
-    for (rate = max_rate;; rate--) {
-      if ((rate = gst_alsa_src_adjust_rate (rate, aggressive)) < min_rate)
-        break;
-      gst_structure_set (structure, "rate", G_TYPE_INT, rate, NULL);
-      for (channels = aggressive ? max_channels : MIN (max_channels, 2);
-          channels >= min_channels; channels--) {
-        gst_structure_set (structure, "channels", G_TYPE_INT, channels, NULL);
-        GST_DEBUG
-            ("trying new caps: %ssigned, endianness: %d, width %d, depth %d, channels %d, rate %d",
-            sign ? "" : "un", endian, width, depth, channels, rate);
-        if (gst_pad_try_set_caps (this->pad[0], caps) != GST_PAD_LINK_REFUSED)
-          gst_alsa_link (this->pad[0], caps);
-
-        if (this->format) {
-          /* try to set caps here */
-          return TRUE;
-        }
-      }
-    }
-  }
+  alsa = GST_ALSA_SRC (asrc);
 
-  if (!aggressive)
-    return gst_alsa_src_set_caps (src, TRUE);
+  snd_pcm_close (alsa->handle);
 
-  return FALSE;
+  return TRUE;
 }
 
-inline snd_pcm_sframes_t
-gst_alsa_src_update_avail (GstAlsa * this)
+
+/*
+ *   Underrun and suspend recovery
+ */
+static gint
+xrun_recovery (snd_pcm_t * handle, gint err)
 {
-  snd_pcm_sframes_t avail = -1;
-
-  while (avail < 0) {
-    avail = snd_pcm_avail_update (this->handle);
-    if (avail < 0) {
-      if (avail == -EPIPE) {
-        gst_alsa_xrun_recovery (this);
-      } else {
-        GST_WARNING_OBJECT (this, "unknown ALSA avail_update return value (%d)",
-            (int) avail);
-      }
-    }
-    if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
-      if (!gst_alsa_start (this)) {
-        return 0;
-      }
+  GST_DEBUG ("xrun recovery %d", err);
+
+  if (err == -EPIPE) {          /* under-run */
+    err = snd_pcm_prepare (handle);
+    if (err < 0)
+      GST_WARNING ("Can't recovery from underrun, prepare failed: %s",
+          snd_strerror (err));
+    return 0;
+  } else if (err == -ESTRPIPE) {
+    while ((err = snd_pcm_resume (handle)) == -EAGAIN)
+      g_usleep (100);           /* wait until the suspend flag is released */
+
+    if (err < 0) {
+      err = snd_pcm_prepare (handle);
+      if (err < 0)
+        GST_WARNING ("Can't recovery from suspend, prepare failed: %s",
+            snd_strerror (err));
     }
+    return 0;
   }
-  return avail;
+  return err;
 }
 
-/* we transmit buffers of period_size frames */
-static void
-gst_alsa_src_loop (GstElement * element)
+static guint
+gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length)
 {
-  snd_pcm_sframes_t avail, copied;
-  gint i;
-  GstAlsa *this = GST_ALSA (element);
-  GstAlsaSrc *src = GST_ALSA_SRC (element);
-
-  /* set the caps on all pads */
-  if (!this->format) {
-    if (!gst_alsa_src_set_caps (src, FALSE)) {
-      GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
-          ("ALSA format not negotiated"));
-      return;
+  GstAlsaSrc *alsa;
+  gint err;
+  gint cptr;
+  gint16 *ptr;
+
+  alsa = GST_ALSA_SRC (asrc);
+
+  cptr = length / alsa->bytes_per_sample;
+  ptr = data;
+
+  while (cptr > 0) {
+    if ((err = snd_pcm_readi (alsa->handle, ptr, cptr)) < 0) {
+      if (err == -EAGAIN) {
+        GST_DEBUG ("Read error: %s", snd_strerror (err));
+        continue;
+      } else if (xrun_recovery (alsa->handle, err) < 0) {
+        goto read_error;
+      }
+      continue;
     }
-  }
 
-  /* the cast to long is explicitly needed;
-   * with avail = -32 and period_size = 100, avail < period_size is false */
-  while ((avail = gst_alsa_src_update_avail (this)) < this->period_size) {
-    /* wait */
-    if (gst_alsa_pcm_wait (this) == FALSE)
-      return;
+    ptr += err * alsa->channels;
+    cptr -= err;
   }
-  g_assert (avail >= this->period_size);
-  /* make sure every pad has a buffer */
-  for (i = 0; i < element->numpads; i++) {
-    if (src->buf[i])
-      gst_data_unref (GST_DATA (src->buf[i]));
-    src->buf[i] =
-        gst_buffer_new_and_alloc (gst_alsa_samples_to_bytes (this, avail));
-  }
-
-  /* fill buffer with data */
-  if ((copied = this->transmit (this, &avail)) <= 0)
-    return;
+  return length - cptr;
 
+read_error:
   {
-    gint outsize;
-    GstClockTime outtime, outdur, outreal, outideal, startalsa, outalsa;
-    gint64 diff, offset;
-    struct timeval tstamp;
-    int err;
-
-
-    if ((err = snd_pcm_status (this->handle, src->status)) < 0)
-      GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err));
-
-    offset = this->captured;
-
-    /* duration of buffer is just the time of the samples */
-    outdur = gst_alsa_samples_to_timestamp (this, copied);
-
-    /* The real capture time is the time of the clock minus the duration and
-     * what is now in the buffer */
-    outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur;
-    /* ideal time is counting samples */
-    outideal = gst_alsa_samples_to_timestamp (this, offset);
-
-    snd_pcm_status_get_trigger_tstamp (src->status, &tstamp);
-    startalsa = GST_TIMEVAL_TO_TIME (tstamp) - element->base_time;
-    outalsa = startalsa + outideal;
-
-    outsize = gst_alsa_samples_to_bytes (this, copied);
-    outtime = GST_CLOCK_TIME_NONE;
-
-    if (GST_ELEMENT_CLOCK (this)) {
-      if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) {
-        outtime = outalsa;
-        diff = outideal - outreal;
-        GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal,
-            outreal, diff);
-        offset = gst_alsa_timestamp_to_samples (this, outtime);
-      } else {
-        outtime = outreal;
-        offset = gst_alsa_timestamp_to_samples (this, outtime);
-      }
-    }
+    return length;              /* skip one period */
+  }
+}
 
-    /* push the buffers out and let them have fun */
-    for (i = 0; i < element->numpads; i++) {
-      GstBuffer *buf;
+static guint
+gst_alsasrc_delay (GstAudioSrc * asrc)
+{
+  GstAlsaSrc *alsa;
+  snd_pcm_sframes_t delay;
 
-      if (!src->buf[i])
-        return;
-      if (copied != this->period_size)
-        GST_BUFFER_SIZE (src->buf[i]) = outsize;
+  alsa = GST_ALSA_SRC (asrc);
 
-      GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime;
-      GST_BUFFER_DURATION (src->buf[i]) = outdur;
-      GST_BUFFER_OFFSET (src->buf[i]) = offset;
-      GST_BUFFER_OFFSET_END (src->buf[i]) = offset + copied;
+  snd_pcm_delay (alsa->handle, &delay);
 
-      buf = src->buf[i];
-      src->buf[i] = NULL;
-      gst_pad_push (this->pad[i], GST_DATA (buf));
-    }
-    this->captured += copied;
-  }
+  return delay;
 }
 
 static void
-gst_alsa_src_flush (GstAlsaSrc * src)
+gst_alsasrc_reset (GstAudioSrc * asrc)
 {
-  gint i;
-
-  for (i = 0; i < GST_ELEMENT (src)->numpads; i++) {
-    if (src->buf[i]) {
-      gst_buffer_unref (src->buf[i]);
-      src->buf[i] = NULL;
-    }
-  }
-}
 
-static GstElementStateReturn
-gst_alsa_src_change_state (GstElement * element)
-{
-  GstAlsaSrc *src;
+#if 0
+  GstAlsaSrc *alsa;
+  gint err;
 
-  g_return_val_if_fail (element != NULL, FALSE);
-  src = GST_ALSA_SRC (element);
+  alsa = GST_ALSA_SRC (asrc);
 
-  switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_NULL_TO_READY:
-      break;
-    case GST_STATE_READY_TO_PAUSED:
-      snd_pcm_status_malloc (&src->status);
-      break;
-    case GST_STATE_PAUSED_TO_PLAYING:
-      break;
-    case GST_STATE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_PAUSED_TO_READY:
-      snd_pcm_status_free (src->status);
-      src->status = NULL;
-      gst_alsa_src_flush (src);
-      break;
-    case GST_STATE_READY_TO_NULL:
-      break;
-    default:
-      break;
-  }
+  CHECK (snd_pcm_drop (alsa->handle), drop_error);
+  CHECK (snd_pcm_prepare (alsa->handle), prepare_error);
 
-  if (GST_ELEMENT_CLASS (src_parent_class)->change_state)
-    return GST_ELEMENT_CLASS (src_parent_class)->change_state (element);
+  return;
 
-  return GST_STATE_SUCCESS;
+  /* ERRORS */
+drop_error:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("alsa-reset: pcm drop error: %s", snd_strerror (err)), (NULL));
+    return;
+  }
+prepare_error:
+  {
+    GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+        ("alsa-reset: pcm prepare error: %s", snd_strerror (err)), (NULL));
+    return;
+  }
+#endif
 }
index d4e72e4..55e2857 100644 (file)
@@ -1,8 +1,7 @@
-/*
- * Copyright (C) 2001 CodeFactory AB
- * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
- * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+/* GStreamer
+ * Copyright (C)  2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstalsasrc.h: 
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  *
  * 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
+ * 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.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __GST_ALSA_SRC_H__
-#define __GST_ALSA_SRC_H__
 
-#include "gstalsamixer.h"
+#ifndef __GST_ALSASRC_H__
+#define __GST_ALSASRC_H__
+
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosrc.h>
+#include <alsa/asoundlib.h>
 
 G_BEGIN_DECLS
 
-#define GST_ALSA_SRC(obj)            (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SRC, GstAlsaSrc))
-#define GST_ALSA_SRC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SRC, GstAlsaSrcClass))
-#define GST_IS_ALSA_SRC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SRC))
-#define GST_IS_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SRC))
-#define GST_TYPE_ALSA_SRC            (gst_alsa_src_get_type())
+#define GST_TYPE_ALSA_SRC          (gst_alsasrc_get_type())
+#define GST_ALSA_SRC(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_SRC,GstAlsaSrc))
+#define GST_ALSA_SRC_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_SRC,GstAlsaSrcClass))
+#define GST_IS_ALSA_SRC(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_SRC))
+#define GST_IS_ALSA_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_SRC))
 
 typedef struct _GstAlsaSrc GstAlsaSrc;
 typedef struct _GstAlsaSrcClass GstAlsaSrcClass;
 
 struct _GstAlsaSrc {
-  GstAlsaMixer   parent;
-  GstBuffer     *buf[GST_ALSA_MAX_TRACKS];
-  snd_pcm_status_t *status;
-  GstClockTime  base_time; /* FIXME: move this up ? already present in element ? */
+  GstAudioSrc    src;
+
+  gchar                        *device;
+
+  snd_pcm_t            *handle;
+  snd_pcm_hw_params_t  *hwparams;
+  snd_pcm_sw_params_t  *swparams;
+
+  snd_pcm_access_t access;
+  snd_pcm_format_t format;
+  guint rate;
+  guint channels;
+  gint bytes_per_sample;
+
+  guint buffer_time;
+  guint period_time;
+  snd_pcm_uframes_t buffer_size;
+  snd_pcm_uframes_t period_size;
 };
 
 struct _GstAlsaSrcClass {
-  GstAlsaMixerClass parent_class;
+  GstAudioSrcClass parent_class;
 };
 
-GType gst_alsa_src_get_type (void);
-
-gboolean gst_alsa_src_factory_init (GstPlugin *plugin);
+GType gst_alsasrc_get_type(void);
 
 G_END_DECLS
 
-#endif /* __GST_ALSA_SRC_H__ */
+#endif /* __GST_ALSASRC_H__ */
index 8b177cd..7131e7b 100644 (file)
@@ -17,7 +17,9 @@ CLEANFILES = gstaudiofilterexample.c \
 libgstaudio_@GST_MAJORMINOR@_la_SOURCES = audio.c gstaudioclock.c \
        multichannel.c \
        gstaudiosink.c \
+       gstaudiosrc.c \
        gstbaseaudiosink.c \
+       gstbaseaudiosrc.c \
        gstringbuffer.c
 nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers)
 
@@ -27,7 +29,9 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
        gstaudioclock.h \
        gstaudiofilter.h \
        gstaudiosink.h \
+       gstaudiosrc.h \
        gstbaseaudiosink.h \
+       gstbaseaudiosrc.h \
        gstringbuffer.h \
        multichannel.h
 
index 6f44be6..042e03c 100644 (file)
@@ -73,12 +73,12 @@ static GstRingBufferClass *ring_parent_class = NULL;
 static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf,
     GstRingBufferSpec * spec);
 static gboolean gst_audioringbuffer_release (GstRingBuffer * buf);
-static gboolean gst_audioringbuffer_play (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_start (GstRingBuffer * buf);
 static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf);
 static guint gst_audioringbuffer_delay (GstRingBuffer * buf);
 
 /* ringbuffer abstract base class */
-GType
+static GType
 gst_audioringbuffer_get_type (void)
 {
   static GType ringbuffer_type = 0;
@@ -98,7 +98,7 @@ gst_audioringbuffer_get_type (void)
     };
 
     ringbuffer_type =
-        g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioRingBuffer",
+        g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioSinkRingBuffer",
         &ringbuffer_info, 0);
   }
   return ringbuffer_type;
@@ -124,8 +124,8 @@ gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass)
       GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire);
   gstringbuffer_class->release =
       GST_DEBUG_FUNCPTR (gst_audioringbuffer_release);
-  gstringbuffer_class->play = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
-  gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
+  gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start);
+  gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start);
   gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop);
 
   gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay);
@@ -136,7 +136,7 @@ typedef guint (*WriteFunc) (GstAudioSink * sink, gpointer data, guint length);
 /* this internal thread does nothing else but write samples to the audio device.
  * It will write each segment in the ringbuffer and will update the play
  * pointer. 
- * The play/stop methods control the thread.
+ * The start/stop methods control the thread.
  */
 static void
 audioringbuffer_thread_func (GstRingBuffer * buf)
@@ -307,13 +307,13 @@ gst_audioringbuffer_release (GstRingBuffer * buf)
 }
 
 static gboolean
-gst_audioringbuffer_play (GstRingBuffer * buf)
+gst_audioringbuffer_start (GstRingBuffer * buf)
 {
   GstAudioSink *sink;
 
   sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
 
-  GST_DEBUG ("play, sending signal");
+  GST_DEBUG ("start, sending signal");
   GST_AUDIORINGBUFFER_SIGNAL (buf);
 
   return TRUE;
diff --git a/gst-libs/gst/audio/gstaudiosrc.c b/gst-libs/gst/audio/gstaudiosrc.c
new file mode 100644 (file)
index 0000000..68bc1b3
--- /dev/null
@@ -0,0 +1,418 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstaudiosrc.c: simple audio src base class
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "gstaudiosrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_audiosrc_debug);
+#define GST_CAT_DEFAULT gst_audiosrc_debug
+
+#define GST_TYPE_AUDIORINGBUFFER        \
+       (gst_audioringbuffer_get_type())
+#define GST_AUDIORINGBUFFER(obj)        \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBuffer))
+#define GST_AUDIORINGBUFFER_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBufferClass))
+#define GST_AUDIORINGBUFFER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_AUDIORINGBUFFER, GstAudioRingBufferClass))
+#define GST_IS_AUDIORINGBUFFER(obj)     \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIORINGBUFFER))
+#define GST_IS_AUDIORINGBUFFER_CLASS(obj)\
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIORINGBUFFER))
+
+typedef struct _GstAudioRingBuffer GstAudioRingBuffer;
+typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass;
+
+#define GST_AUDIORINGBUFFER_GET_COND(buf) (((GstAudioRingBuffer *)buf)->cond)
+#define GST_AUDIORINGBUFFER_WAIT(buf)     (g_cond_wait (GST_AUDIORINGBUFFER_GET_COND (buf), GST_GET_LOCK (buf)))
+#define GST_AUDIORINGBUFFER_SIGNAL(buf)   (g_cond_signal (GST_AUDIORINGBUFFER_GET_COND (buf)))
+#define GST_AUDIORINGBUFFER_BROADCAST(buf)(g_cond_broadcast (GST_AUDIORINGBUFFER_GET_COND (buf)))
+
+struct _GstAudioRingBuffer
+{
+  GstRingBuffer object;
+
+  gboolean running;
+  gint queuedseg;
+
+  GCond *cond;
+};
+
+struct _GstAudioRingBufferClass
+{
+  GstRingBufferClass parent_class;
+};
+
+static void gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass);
+static void gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer);
+static void gst_audioringbuffer_dispose (GObject * object);
+static void gst_audioringbuffer_finalize (GObject * object);
+
+static GstRingBufferClass *ring_parent_class = NULL;
+
+static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf,
+    GstRingBufferSpec * spec);
+static gboolean gst_audioringbuffer_release (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_start (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf);
+static guint gst_audioringbuffer_delay (GstRingBuffer * buf);
+
+/* ringbuffer abstract base class */
+static GType
+gst_audioringbuffer_get_type (void)
+{
+  static GType ringbuffer_type = 0;
+
+  if (!ringbuffer_type) {
+    static const GTypeInfo ringbuffer_info = {
+      sizeof (GstAudioRingBufferClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_audioringbuffer_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAudioRingBuffer),
+      0,
+      (GInstanceInitFunc) gst_audioringbuffer_init,
+      NULL
+    };
+
+    ringbuffer_type =
+        g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioSrcRingBuffer",
+        &ringbuffer_info, 0);
+  }
+  return ringbuffer_type;
+}
+
+static void
+gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstObjectClass *gstobject_class;
+  GstRingBufferClass *gstringbuffer_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstobject_class = (GstObjectClass *) klass;
+  gstringbuffer_class = (GstRingBufferClass *) klass;
+
+  ring_parent_class = g_type_class_ref (GST_TYPE_RINGBUFFER);
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_audioringbuffer_dispose);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audioringbuffer_finalize);
+
+  gstringbuffer_class->acquire =
+      GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire);
+  gstringbuffer_class->release =
+      GST_DEBUG_FUNCPTR (gst_audioringbuffer_release);
+  gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start);
+  gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start);
+  gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop);
+
+  gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay);
+}
+
+typedef guint (*ReadFunc) (GstAudioSrc * src, gpointer data, guint length);
+
+/* this internal thread does nothing else but read samples from the audio device.
+ * It will read each segment in the ringbuffer and will update the play
+ * pointer. 
+ * The start/stop methods control the thread.
+ */
+static void
+audioringbuffer_thread_func (GstRingBuffer * buf)
+{
+  GstAudioSrc *src;
+  GstAudioSrcClass *csrc;
+  GstAudioRingBuffer *abuf = GST_AUDIORINGBUFFER (buf);
+  ReadFunc readfunc;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+  csrc = GST_AUDIOSRC_GET_CLASS (src);
+
+  GST_DEBUG ("enter thread");
+
+  readfunc = csrc->read;
+  if (readfunc == NULL)
+    goto no_function;
+
+  while (TRUE) {
+    gint left, len;
+    guint8 *readptr;
+    gint readseg;
+
+    if (gst_ringbuffer_prepare_read (buf, &readseg, &readptr, &len)) {
+      gint read = 0;
+
+      left = len;
+      do {
+        GST_DEBUG ("transfer %d bytes to segment %d", left, readseg);
+        read = readfunc (src, readptr + read, left);
+        GST_DEBUG ("transfered %d bytes", read);
+        if (read < 0 || read > left) {
+          GST_WARNING ("error reading data (reason: %s), skipping segment\n",
+              strerror (errno));
+          break;
+        }
+        left -= read;
+      } while (left > 0);
+
+      /* we read one segment */
+      gst_ringbuffer_advance (buf, 1);
+    } else {
+      GST_LOCK (abuf);
+      if (!abuf->running)
+        goto stop_running;
+      GST_DEBUG ("signal wait");
+      GST_AUDIORINGBUFFER_SIGNAL (buf);
+      GST_DEBUG ("wait for action");
+      GST_AUDIORINGBUFFER_WAIT (buf);
+      GST_DEBUG ("got signal");
+      if (!abuf->running)
+        goto stop_running;
+      GST_DEBUG ("continue running");
+      GST_UNLOCK (abuf);
+    }
+  }
+  GST_DEBUG ("exit thread");
+
+  return;
+
+  /* ERROR */
+no_function:
+  {
+    GST_DEBUG ("no write function, exit thread");
+    return;
+  }
+stop_running:
+  {
+    GST_UNLOCK (abuf);
+    GST_DEBUG ("stop running, exit thread");
+    return;
+  }
+}
+
+static void
+gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer)
+{
+  ringbuffer->running = FALSE;
+  ringbuffer->queuedseg = 0;
+
+  ringbuffer->cond = g_cond_new ();
+}
+
+static void
+gst_audioringbuffer_dispose (GObject * object)
+{
+  G_OBJECT_CLASS (ring_parent_class)->dispose (object);
+}
+
+static void
+gst_audioringbuffer_finalize (GObject * object)
+{
+  G_OBJECT_CLASS (ring_parent_class)->finalize (object);
+}
+
+static gboolean
+gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
+{
+  GstAudioSrc *src;
+  GstAudioSrcClass *csrc;
+  GstAudioRingBuffer *abuf;
+  gboolean result = FALSE;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+  csrc = GST_AUDIOSRC_GET_CLASS (src);
+
+  if (csrc->open)
+    result = csrc->open (src, spec);
+
+  if (!result)
+    goto could_not_open;
+
+  /* allocate one more segment as we need some headroom */
+  spec->segtotal++;
+
+  buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
+  memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
+
+  abuf = GST_AUDIORINGBUFFER (buf);
+  abuf->running = TRUE;
+
+  src->thread =
+      g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE,
+      NULL);
+  GST_AUDIORINGBUFFER_WAIT (buf);
+
+  return result;
+
+could_not_open:
+  {
+    return FALSE;
+  }
+}
+
+/* function is called with LOCK */
+static gboolean
+gst_audioringbuffer_release (GstRingBuffer * buf)
+{
+  GstAudioSrc *src;
+  GstAudioSrcClass *csrc;
+  GstAudioRingBuffer *abuf;
+  gboolean result = FALSE;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+  csrc = GST_AUDIOSRC_GET_CLASS (src);
+  abuf = GST_AUDIORINGBUFFER (buf);
+
+  abuf->running = FALSE;
+  GST_AUDIORINGBUFFER_SIGNAL (buf);
+  GST_UNLOCK (buf);
+
+  /* join the thread */
+  g_thread_join (src->thread);
+
+  GST_LOCK (buf);
+
+  /* free the buffer */
+  gst_buffer_unref (buf->data);
+  buf->data = NULL;
+
+  if (csrc->close)
+    result = csrc->close (src);
+
+  return result;
+}
+
+static gboolean
+gst_audioringbuffer_start (GstRingBuffer * buf)
+{
+  GstAudioSrc *src;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+
+  GST_DEBUG ("start, sending signal");
+  GST_AUDIORINGBUFFER_SIGNAL (buf);
+
+  return TRUE;
+}
+
+static gboolean
+gst_audioringbuffer_stop (GstRingBuffer * buf)
+{
+  GstAudioSrc *src;
+  GstAudioSrcClass *csrc;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+  csrc = GST_AUDIOSRC_GET_CLASS (src);
+
+  /* unblock any pending writes to the audio device */
+  if (csrc->reset) {
+    GST_DEBUG ("reset...");
+    csrc->reset (src);
+    GST_DEBUG ("reset done");
+  }
+
+  GST_DEBUG ("stop, waiting...");
+  GST_AUDIORINGBUFFER_WAIT (buf);
+  GST_DEBUG ("stoped");
+
+  return TRUE;
+}
+
+static guint
+gst_audioringbuffer_delay (GstRingBuffer * buf)
+{
+  GstAudioSrc *src;
+  GstAudioSrcClass *csrc;
+  guint res = 0;
+
+  src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf));
+  csrc = GST_AUDIOSRC_GET_CLASS (src);
+
+  if (csrc->delay)
+    res = csrc->delay (src);
+
+  return res;
+}
+
+/* AudioSrc signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+};
+
+#define _do_init(bla) \
+    GST_DEBUG_CATEGORY_INIT (gst_audiosrc_debug, "audiosrc", 0, "audiosrc element");
+
+GST_BOILERPLATE_FULL (GstAudioSrc, gst_audiosrc, GstBaseAudioSrc,
+    GST_TYPE_BASEAUDIOSRC, _do_init);
+
+static GstRingBuffer *gst_audiosrc_create_ringbuffer (GstBaseAudioSrc * src);
+
+static void
+gst_audiosrc_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_audiosrc_class_init (GstAudioSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstPushSrcClass *gstpushsrc_class;
+  GstBaseAudioSrcClass *gstbaseaudiosrc_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+  gstpushsrc_class = (GstPushSrcClass *) klass;
+  gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
+
+  gstbaseaudiosrc_class->create_ringbuffer =
+      GST_DEBUG_FUNCPTR (gst_audiosrc_create_ringbuffer);
+}
+
+static void
+gst_audiosrc_init (GstAudioSrc * audiosrc)
+{
+  gst_base_src_set_live (GST_BASE_SRC (audiosrc), TRUE);
+}
+
+static GstRingBuffer *
+gst_audiosrc_create_ringbuffer (GstBaseAudioSrc * src)
+{
+  GstRingBuffer *buffer;
+
+  GST_DEBUG ("creating ringbuffer");
+  buffer = g_object_new (GST_TYPE_AUDIORINGBUFFER, NULL);
+  GST_DEBUG ("created ringbuffer @%p", buffer);
+
+  return buffer;
+}
diff --git a/gst-libs/gst/audio/gstaudiosrc.h b/gst-libs/gst/audio/gstaudiosrc.h
new file mode 100644 (file)
index 0000000..784afa1
--- /dev/null
@@ -0,0 +1,84 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstaudiosrc.h: 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* a base class for simple audio srcs.
+ *
+ * This base class only requires subclasses to implement a set
+ * of simple functions.
+ *
+ * - open: open the device with the specified caps
+ * - read: read samples to the audio device
+ * - close: close the device
+ * - delay: the number of samples queued in the device
+ * - reset: unblock a read to the device and reset.
+ *
+ * All scheduling of samples and timestamps is done in this
+ * base class.
+ */
+
+#ifndef __GST_AUDIOSRC_H__
+#define __GST_AUDIOSRC_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstbaseaudiosrc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AUDIOSRC              (gst_audiosrc_get_type())
+#define GST_AUDIOSRC(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOSRC,GstAudioSrc))
+#define GST_AUDIOSRC_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOSRC,GstAudioSrcClass))
+#define GST_AUDIOSRC_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIOSRC,GstAudioSrcClass))
+#define GST_IS_AUDIOSRC(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOSRC))
+#define GST_IS_AUDIOSRC_CLASS(obj)     (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOSRC))
+
+typedef struct _GstAudioSrc GstAudioSrc;
+typedef struct _GstAudioSrcClass GstAudioSrcClass;
+
+struct _GstAudioSrc {
+  GstBaseAudioSrc       element;
+
+  /*< private >*/ /* with LOCK */
+  GThread   *thread;
+};
+
+struct _GstAudioSrcClass {
+  GstBaseAudioSrcClass parent_class;
+
+  /* vtable */
+
+  /* open the device with given specs */
+  gboolean (*open)   (GstAudioSrc *src, GstRingBufferSpec *spec);
+  /* close the device */
+  gboolean (*close)  (GstAudioSrc *src);
+  /* read samples from the device */
+  guint    (*read)   (GstAudioSrc *src, gpointer data, guint length);
+  /* get number of samples queued in the device */
+  guint    (*delay)  (GstAudioSrc *src);
+  /* reset the audio device, unblock from a write */
+  void     (*reset)  (GstAudioSrc *src);
+};
+
+GType gst_audiosrc_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_AUDIOSRC_H__ */
index 8590cef..0a527e4 100644 (file)
@@ -160,7 +160,7 @@ gst_baseaudiosink_get_time (GstClock * clock, GstBaseAudioSink * sink)
   if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
     return 0;
 
-  samples = gst_ringbuffer_played_samples (sink->ringbuffer);
+  samples = gst_ringbuffer_samples_done (sink->ringbuffer);
 
   result = samples * GST_SECOND / sink->ringbuffer->spec.rate;
   result += GST_ELEMENT (sink)->base_time;
@@ -210,204 +210,31 @@ gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
-static int linear_formats[4 * 2 * 2] = {
-  GST_S8,
-  GST_S8,
-  GST_U8,
-  GST_U8,
-  GST_S16_LE,
-  GST_S16_BE,
-  GST_U16_LE,
-  GST_U16_BE,
-  GST_S24_LE,
-  GST_S24_BE,
-  GST_U24_LE,
-  GST_U24_BE,
-  GST_S32_LE,
-  GST_S32_BE,
-  GST_U32_LE,
-  GST_U32_BE
-};
-
-static int linear24_formats[3 * 2 * 2] = {
-  GST_S24_3LE,
-  GST_S24_3BE,
-  GST_U24_3LE,
-  GST_U24_3BE,
-  GST_S20_3LE,
-  GST_S20_3BE,
-  GST_U20_3LE,
-  GST_U20_3BE,
-  GST_S18_3LE,
-  GST_S18_3BE,
-  GST_U18_3LE,
-  GST_U18_3BE,
-};
-
-static GstBufferFormat
-build_linear_format (int depth, int width, int unsignd, int big_endian)
-{
-  if (width == 24) {
-    switch (depth) {
-      case 24:
-        depth = 0;
-        break;
-      case 20:
-        depth = 1;
-        break;
-      case 18:
-        depth = 2;
-        break;
-      default:
-        return GST_UNKNOWN;
-    }
-    return ((int (*)[2][2]) linear24_formats)[depth][!!unsignd][!!big_endian];
-  } else {
-    switch (depth) {
-      case 8:
-        depth = 0;
-        break;
-      case 16:
-        depth = 1;
-        break;
-      case 24:
-        depth = 2;
-        break;
-      case 32:
-        depth = 3;
-        break;
-      default:
-        return GST_UNKNOWN;
-    }
-  }
-  return ((int (*)[2][2]) linear_formats)[depth][!!unsignd][!!big_endian];
-}
-
-static void
-debug_spec_caps (GstBaseAudioSink * sink, GstRingBufferSpec * spec)
-{
-  GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps);
-  GST_DEBUG ("parsed caps: type:         %d", spec->type);
-  GST_DEBUG ("parsed caps: format:       %d", spec->format);
-  GST_DEBUG ("parsed caps: width:        %d", spec->width);
-  GST_DEBUG ("parsed caps: depth:        %d", spec->depth);
-  GST_DEBUG ("parsed caps: sign:         %d", spec->sign);
-  GST_DEBUG ("parsed caps: bigend:       %d", spec->bigend);
-  GST_DEBUG ("parsed caps: rate:         %d", spec->rate);
-  GST_DEBUG ("parsed caps: channels:     %d", spec->channels);
-  GST_DEBUG ("parsed caps: sample bytes: %d", spec->bytes_per_sample);
-}
-
-static void
-debug_spec_buffer (GstBaseAudioSink * sink, GstRingBufferSpec * spec)
-{
-  GST_DEBUG ("acquire ringbuffer: buffer time: %" G_GINT64_FORMAT " usec",
-      spec->buffer_time);
-  GST_DEBUG ("acquire ringbuffer: latency time: %" G_GINT64_FORMAT " usec",
-      spec->latency_time);
-  GST_DEBUG ("acquire ringbuffer: total segments: %d", spec->segtotal);
-  GST_DEBUG ("acquire ringbuffer: segment size: %d bytes = %d samples",
-      spec->segsize, spec->segsize / spec->bytes_per_sample);
-  GST_DEBUG ("acquire ringbuffer: buffer size: %d bytes = %d samples",
-      spec->segsize * spec->segtotal,
-      spec->segsize * spec->segtotal / spec->bytes_per_sample);
-}
-
 static gboolean
 gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
 {
   GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
   GstRingBufferSpec *spec;
-  const gchar *mimetype;
-  GstStructure *structure;
 
   spec = &sink->ringbuffer->spec;
 
-  structure = gst_caps_get_structure (caps, 0);
-
-  /* we have to differentiate between int and float formats */
-  mimetype = gst_structure_get_name (structure);
-
-  if (!strncmp (mimetype, "audio/x-raw-int", 15)) {
-    gint endianness;
-
-    spec->type = GST_BUFTYPE_LINEAR;
-
-    /* extract the needed information from the cap */
-    if (!(gst_structure_get_int (structure, "width", &spec->width) &&
-            gst_structure_get_int (structure, "depth", &spec->depth) &&
-            gst_structure_get_boolean (structure, "signed", &spec->sign)))
-      goto parse_error;
-
-    /* extract endianness if needed */
-    if (spec->width > 8) {
-      if (!gst_structure_get_int (structure, "endianness", &endianness))
-        goto parse_error;
-    } else {
-      endianness = G_BYTE_ORDER;
-    }
-
-    spec->bigend = endianness == G_LITTLE_ENDIAN ? FALSE : TRUE;
-
-    spec->format =
-        build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1,
-        spec->bigend ? 1 : 0);
-
-  } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) {
-
-    spec->type = GST_BUFTYPE_FLOAT;
-
-    /* get layout */
-    if (!gst_structure_get_int (structure, "width", &spec->width))
-      goto parse_error;
-
-    /* match layout to format wrt to endianness */
-    switch (spec->width) {
-      case 32:
-        spec->format =
-            G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT32_LE : GST_FLOAT32_BE;
-        break;
-      case 64:
-        spec->format =
-            G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT64_LE : GST_FLOAT64_BE;
-        break;
-      default:
-        goto parse_error;
-    }
-  } else if (!strncmp (mimetype, "audio/x-alaw", 12)) {
-    spec->type = GST_BUFTYPE_A_LAW;
-    spec->format = GST_A_LAW;
-  } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) {
-    spec->type = GST_BUFTYPE_MU_LAW;
-    spec->format = GST_MU_LAW;
-  } else {
-    goto parse_error;
-  }
-
-  /* get rate and channels */
-  if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
-          gst_structure_get_int (structure, "channels", &spec->channels)))
-    goto parse_error;
-
-  spec->bytes_per_sample = (spec->width >> 3) * spec->channels;
+  GST_DEBUG ("release old ringbuffer");
 
-  gst_caps_replace (&spec->caps, caps);
+  /* release old ringbuffer */
+  gst_ringbuffer_release (sink->ringbuffer);
 
-  debug_spec_caps (sink, spec);
+  GST_DEBUG ("parse caps");
 
   spec->buffer_time = sink->buffer_time;
   spec->latency_time = sink->latency_time;
 
-  /* calculate suggested segsize and segtotal */
-  spec->segsize =
-      spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
-  spec->segtotal = spec->buffer_time / spec->latency_time;
-
-  GST_DEBUG ("release old ringbuffer");
+  /* parse new caps */
+  if (!gst_ringbuffer_parse_caps (spec, caps))
+    goto parse_error;
 
-  gst_ringbuffer_release (sink->ringbuffer);
+  gst_ringbuffer_debug_spec_buff (spec);
 
-  debug_spec_buffer (sink, spec);
+  GST_DEBUG ("acquire new ringbuffer");
 
   if (!gst_ringbuffer_acquire (sink->ringbuffer, spec))
     goto acquire_error;
@@ -419,7 +246,7 @@ gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
       spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate *
       spec->bytes_per_sample);
 
-  debug_spec_buffer (sink, spec);
+  gst_ringbuffer_debug_spec_buff (spec);
 
   return TRUE;
 
@@ -487,10 +314,23 @@ gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event)
 static GstFlowReturn
 gst_baseaudiosink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
 {
+  GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
+
+  if (!gst_ringbuffer_is_acquired (sink->ringbuffer))
+    goto wrong_state;
+
   /* we don't really do anything when prerolling. We could make a
    * property to play this buffer to have some sort of scrubbing
    * support. */
   return GST_FLOW_OK;
+
+wrong_state:
+  {
+    GST_DEBUG ("ringbuffer in wrong state");
+    GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
+        ("sink not negotiated."), (NULL));
+    return GST_FLOW_ERROR;
+  }
 }
 
 static GstFlowReturn
@@ -514,6 +354,8 @@ gst_baseaudiosink_render (GstBaseSink * bsink, GstBuffer * buf)
 wrong_state:
   {
     GST_DEBUG ("ringbuffer in wrong state");
+    GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
+        ("sink not negotiated."), (NULL));
     return GST_FLOW_ERROR;
   }
 }
diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.c b/gst-libs/gst/audio/gstbaseaudiosrc.c
new file mode 100644 (file)
index 0000000..98f8d33
--- /dev/null
@@ -0,0 +1,403 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbaseaudiosrc.c: 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "gstbaseaudiosrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_baseaudiosrc_debug);
+#define GST_CAT_DEFAULT gst_baseaudiosrc_debug
+
+/* BaseAudioSrc signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+#define DEFAULT_BUFFER_TIME    500 * GST_USECOND
+#define DEFAULT_LATENCY_TIME   10 * GST_USECOND
+enum
+{
+  PROP_0,
+  PROP_BUFFER_TIME,
+  PROP_LATENCY_TIME,
+};
+
+#define _do_init(bla) \
+    GST_DEBUG_CATEGORY_INIT (gst_baseaudiosrc_debug, "baseaudiosrc", 0, "baseaudiosrc element");
+
+GST_BOILERPLATE_FULL (GstBaseAudioSrc, gst_baseaudiosrc, GstPushSrc,
+    GST_TYPE_PUSHSRC, _do_init);
+
+static void gst_baseaudiosrc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_baseaudiosrc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void gst_baseaudiosrc_fixate (GstPad * pad, GstCaps * caps);
+
+static GstElementStateReturn gst_baseaudiosrc_change_state (GstElement *
+    element);
+
+static GstClock *gst_baseaudiosrc_get_clock (GstElement * elem);
+static GstClockTime gst_baseaudiosrc_get_time (GstClock * clock,
+    GstBaseAudioSrc * src);
+
+static GstFlowReturn gst_baseaudiosrc_create (GstPushSrc * psrc,
+    GstBuffer ** buf);
+
+static gboolean gst_baseaudiosrc_event (GstBaseSrc * bsrc, GstEvent * event);
+static void gst_baseaudiosrc_get_times (GstBaseSrc * bsrc,
+    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
+static gboolean gst_baseaudiosrc_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
+
+//static guint gst_baseaudiosrc_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gst_baseaudiosrc_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_baseaudiosrc_class_init (GstBaseAudioSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstPushSrcClass *gstpushsrc_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+  gstpushsrc_class = (GstPushSrcClass *) klass;
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosrc_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME,
+      g_param_spec_int64 ("buffer-time", "Buffer Time",
+          "Size of audio buffer in milliseconds (-1 = default)",
+          -1, G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY_TIME,
+      g_param_spec_int64 ("latency-time", "Latency Time",
+          "Audio latency in milliseconds (-1 = default)",
+          -1, G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosrc_change_state);
+  gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_clock);
+
+  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_setcaps);
+  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_event);
+  gstbasesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_times);
+
+  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_create);
+}
+
+static void
+gst_baseaudiosrc_init (GstBaseAudioSrc * baseaudiosrc)
+{
+  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
+  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;
+
+  baseaudiosrc->clock = gst_audio_clock_new ("clock",
+      (GstAudioClockGetTimeFunc) gst_baseaudiosrc_get_time, baseaudiosrc);
+
+  gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc),
+      gst_baseaudiosrc_fixate);
+}
+
+static GstClock *
+gst_baseaudiosrc_get_clock (GstElement * elem)
+{
+  GstBaseAudioSrc *src;
+
+  src = GST_BASEAUDIOSRC (elem);
+
+  return GST_CLOCK (gst_object_ref (GST_OBJECT (src->clock)));
+}
+
+static GstClockTime
+gst_baseaudiosrc_get_time (GstClock * clock, GstBaseAudioSrc * src)
+{
+  guint64 samples;
+  GstClockTime result;
+
+  if (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0)
+    return 0;
+
+  samples = gst_ringbuffer_samples_done (src->ringbuffer);
+
+  result = samples * GST_SECOND / src->ringbuffer->spec.rate;
+  result += GST_ELEMENT (src)->base_time;
+
+  return result;
+}
+
+static void
+gst_baseaudiosrc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstBaseAudioSrc *src;
+
+  src = GST_BASEAUDIOSRC (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER_TIME:
+      src->buffer_time = g_value_get_int64 (value);
+      break;
+    case PROP_LATENCY_TIME:
+      src->latency_time = g_value_get_int64 (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_baseaudiosrc_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstBaseAudioSrc *src;
+
+  src = GST_BASEAUDIOSRC (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER_TIME:
+      g_value_set_int64 (value, src->buffer_time);
+      break;
+    case PROP_LATENCY_TIME:
+      g_value_set_int64 (value, src->latency_time);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_baseaudiosrc_fixate (GstPad * pad, GstCaps * caps)
+{
+  GstStructure *s;
+
+  s = gst_caps_get_structure (caps, 0);
+
+  gst_caps_structure_fixate_field_nearest_int (s, "rate", 44100);
+  gst_caps_structure_fixate_field_nearest_int (s, "channels", 2);
+  gst_caps_structure_fixate_field_nearest_int (s, "depth", 16);
+  gst_caps_structure_fixate_field_nearest_int (s, "width", 16);
+  gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+  gst_caps_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
+}
+
+static gboolean
+gst_baseaudiosrc_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
+{
+  GstBaseAudioSrc *src = GST_BASEAUDIOSRC (bsrc);
+  GstRingBufferSpec *spec;
+
+  spec = &src->ringbuffer->spec;
+
+  spec->buffer_time = src->buffer_time;
+  spec->latency_time = src->latency_time;
+
+  if (!gst_ringbuffer_parse_caps (spec, caps))
+    goto parse_error;
+
+  /* calculate suggested segsize and segtotal */
+  spec->segsize =
+      spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
+  spec->segtotal = spec->buffer_time / spec->latency_time;
+
+  GST_DEBUG ("release old ringbuffer");
+
+  gst_ringbuffer_release (src->ringbuffer);
+
+  gst_ringbuffer_debug_spec_buff (spec);
+
+  GST_DEBUG ("acquire new ringbuffer");
+
+  if (!gst_ringbuffer_acquire (src->ringbuffer, spec))
+    goto acquire_error;
+
+  /* calculate actual latency and buffer times */
+  spec->latency_time =
+      spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample);
+  spec->buffer_time =
+      spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate *
+      spec->bytes_per_sample);
+
+  gst_ringbuffer_debug_spec_buff (spec);
+
+  return TRUE;
+
+  /* ERRORS */
+parse_error:
+  {
+    GST_DEBUG ("could not parse caps");
+    return FALSE;
+  }
+acquire_error:
+  {
+    GST_DEBUG ("could not acquire ringbuffer");
+    return FALSE;
+  }
+}
+
+static void
+gst_baseaudiosrc_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
+    GstClockTime * start, GstClockTime * end)
+{
+  /* ne need to sync to a clock here, we schedule the samples based
+   * on our own clock for the moment. FIXME, implement this when
+   * we are not using our own clock */
+  *start = GST_CLOCK_TIME_NONE;
+  *end = GST_CLOCK_TIME_NONE;
+}
+
+static gboolean
+gst_baseaudiosrc_event (GstBaseSrc * bsrc, GstEvent * event)
+{
+  GstBaseAudioSrc *src = GST_BASEAUDIOSRC (bsrc);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH:
+      if (GST_EVENT_FLUSH_DONE (event)) {
+      } else {
+        gst_ringbuffer_pause (src->ringbuffer);
+      }
+      break;
+    default:
+      break;
+  }
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_baseaudiosrc_create (GstPushSrc * psrc, GstBuffer ** outbuf)
+{
+  GstBaseAudioSrc *src = GST_BASEAUDIOSRC (psrc);
+  GstBuffer *buf;
+  guchar *data;
+  guint len;
+  guint res;
+
+  if (!gst_ringbuffer_is_acquired (src->ringbuffer))
+    goto wrong_state;
+
+  buf = gst_buffer_new_and_alloc (src->ringbuffer->spec.segsize);
+
+  data = GST_BUFFER_DATA (buf);
+  len = GST_BUFFER_SIZE (buf);
+
+  res = gst_ringbuffer_read (src->ringbuffer, -1, data, len);
+  if (res == -1)
+    goto stopped;
+
+  gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc)));
+
+  *outbuf = buf;
+
+  return GST_FLOW_OK;
+
+wrong_state:
+  {
+    GST_DEBUG ("ringbuffer in wrong state");
+    return GST_FLOW_WRONG_STATE;
+  }
+stopped:
+  {
+    gst_buffer_unref (buf);
+    GST_DEBUG ("ringbuffer stopped");
+    return GST_FLOW_WRONG_STATE;
+  }
+}
+
+GstRingBuffer *
+gst_baseaudiosrc_create_ringbuffer (GstBaseAudioSrc * src)
+{
+  GstBaseAudioSrcClass *bclass;
+  GstRingBuffer *buffer = NULL;
+
+  bclass = GST_BASEAUDIOSRC_GET_CLASS (src);
+  if (bclass->create_ringbuffer)
+    buffer = bclass->create_ringbuffer (src);
+
+  if (buffer) {
+    gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (src));
+  }
+
+  return buffer;
+}
+
+void
+gst_baseaudiosrc_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
+    gpointer user_data)
+{
+  //GstBaseAudioSrc *src = GST_BASEAUDIOSRC (data);
+}
+
+static GstElementStateReturn
+gst_baseaudiosrc_change_state (GstElement * element)
+{
+  GstElementStateReturn ret = GST_STATE_SUCCESS;
+  GstBaseAudioSrc *src = GST_BASEAUDIOSRC (element);
+  GstElementState transition = GST_STATE_TRANSITION (element);
+
+  switch (transition) {
+    case GST_STATE_NULL_TO_READY:
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      src->ringbuffer = gst_baseaudiosrc_create_ringbuffer (src);
+      gst_ringbuffer_set_callback (src->ringbuffer, gst_baseaudiosrc_callback,
+          src);
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  switch (transition) {
+    case GST_STATE_PLAYING_TO_PAUSED:
+      gst_ringbuffer_pause (src->ringbuffer);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      gst_ringbuffer_stop (src->ringbuffer);
+      gst_ringbuffer_release (src->ringbuffer);
+      gst_object_unref (GST_OBJECT (src->ringbuffer));
+      src->ringbuffer = NULL;
+      break;
+    case GST_STATE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.h b/gst-libs/gst/audio/gstbaseaudiosrc.h
new file mode 100644 (file)
index 0000000..9dcb9f1
--- /dev/null
@@ -0,0 +1,77 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbaseaudiosrc.h: 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* a base class for audio sources.
+ */
+
+#ifndef __GST_BASEAUDIOSRC_H__
+#define __GST_BASEAUDIOSRC_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+#include "gstringbuffer.h"
+#include "gstaudioclock.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_BASEAUDIOSRC                  (gst_baseaudiosrc_get_type())
+#define GST_BASEAUDIOSRC(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASEAUDIOSRC,GstBaseAudioSrc))
+#define GST_BASEAUDIOSRC_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASEAUDIOSRC,GstBaseAudioSrcClass))
+#define GST_BASEAUDIOSRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASEAUDIOSRC, GstBaseAudioSrcClass))
+#define GST_IS_BASEAUDIOSRC(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASEAUDIOSRC))
+#define GST_IS_BASEAUDIOSRC_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASEAUDIOSRC))
+
+#define GST_BASEAUDIOSRC_CLOCK(obj)     (GST_BASEAUDIOSRC (obj)->clock)
+#define GST_BASEAUDIOSRC_PAD(obj)       (GST_BASESRC (obj)->srcpad)
+
+typedef struct _GstBaseAudioSrc GstBaseAudioSrc;
+typedef struct _GstBaseAudioSrcClass GstBaseAudioSrcClass;
+
+struct _GstBaseAudioSrc {
+  GstPushSrc    element;
+
+  /*< protected >*/ /* with LOCK */
+  /* our ringbuffer */
+  GstRingBuffer *ringbuffer;
+
+  /* required buffer and latency */
+  GstClockTime   buffer_time;
+  GstClockTime   latency_time;
+
+  /* clock */
+  GstClock     *clock;
+};
+
+struct _GstBaseAudioSrcClass {
+  GstPushSrcClass parent_class;
+
+  /* subclass ringbuffer allocation */
+  GstRingBuffer* (*create_ringbuffer)  (GstBaseAudioSrc *src);
+};
+
+GType gst_baseaudiosrc_get_type(void);
+
+GstRingBuffer *gst_baseaudiosrc_create_ringbuffer (GstBaseAudioSrc *src);
+
+G_END_DECLS
+
+#endif /* __GST_BASEAUDIOSRC_H__ */
index 04adc9b..2dc8f0b 100644 (file)
@@ -106,6 +106,204 @@ gst_ringbuffer_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer));
 }
 
+static int linear_formats[4 * 2 * 2] = {
+  GST_S8,
+  GST_S8,
+  GST_U8,
+  GST_U8,
+  GST_S16_LE,
+  GST_S16_BE,
+  GST_U16_LE,
+  GST_U16_BE,
+  GST_S24_LE,
+  GST_S24_BE,
+  GST_U24_LE,
+  GST_U24_BE,
+  GST_S32_LE,
+  GST_S32_BE,
+  GST_U32_LE,
+  GST_U32_BE
+};
+
+static int linear24_formats[3 * 2 * 2] = {
+  GST_S24_3LE,
+  GST_S24_3BE,
+  GST_U24_3LE,
+  GST_U24_3BE,
+  GST_S20_3LE,
+  GST_S20_3BE,
+  GST_U20_3LE,
+  GST_U20_3BE,
+  GST_S18_3LE,
+  GST_S18_3BE,
+  GST_U18_3LE,
+  GST_U18_3BE,
+};
+
+static GstBufferFormat
+build_linear_format (int depth, int width, int unsignd, int big_endian)
+{
+  if (width == 24) {
+    switch (depth) {
+      case 24:
+        depth = 0;
+        break;
+      case 20:
+        depth = 1;
+        break;
+      case 18:
+        depth = 2;
+        break;
+      default:
+        return GST_UNKNOWN;
+    }
+    return ((int (*)[2][2]) linear24_formats)[depth][!!unsignd][!!big_endian];
+  } else {
+    switch (depth) {
+      case 8:
+        depth = 0;
+        break;
+      case 16:
+        depth = 1;
+        break;
+      case 24:
+        depth = 2;
+        break;
+      case 32:
+        depth = 3;
+        break;
+      default:
+        return GST_UNKNOWN;
+    }
+  }
+  return ((int (*)[2][2]) linear_formats)[depth][!!unsignd][!!big_endian];
+}
+
+void
+gst_ringbuffer_debug_spec_caps (GstRingBufferSpec * spec)
+{
+  GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps);
+  GST_DEBUG ("parsed caps: type:         %d", spec->type);
+  GST_DEBUG ("parsed caps: format:       %d", spec->format);
+  GST_DEBUG ("parsed caps: width:        %d", spec->width);
+  GST_DEBUG ("parsed caps: depth:        %d", spec->depth);
+  GST_DEBUG ("parsed caps: sign:         %d", spec->sign);
+  GST_DEBUG ("parsed caps: bigend:       %d", spec->bigend);
+  GST_DEBUG ("parsed caps: rate:         %d", spec->rate);
+  GST_DEBUG ("parsed caps: channels:     %d", spec->channels);
+  GST_DEBUG ("parsed caps: sample bytes: %d", spec->bytes_per_sample);
+}
+
+void
+gst_ringbuffer_debug_spec_buff (GstRingBufferSpec * spec)
+{
+  GST_DEBUG ("acquire ringbuffer: buffer time: %" G_GINT64_FORMAT " usec",
+      spec->buffer_time);
+  GST_DEBUG ("acquire ringbuffer: latency time: %" G_GINT64_FORMAT " usec",
+      spec->latency_time);
+  GST_DEBUG ("acquire ringbuffer: total segments: %d", spec->segtotal);
+  GST_DEBUG ("acquire ringbuffer: segment size: %d bytes = %d samples",
+      spec->segsize, spec->segsize / spec->bytes_per_sample);
+  GST_DEBUG ("acquire ringbuffer: buffer size: %d bytes = %d samples",
+      spec->segsize * spec->segtotal,
+      spec->segsize * spec->segtotal / spec->bytes_per_sample);
+}
+
+gboolean
+gst_ringbuffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
+{
+  const gchar *mimetype;
+  GstStructure *structure;
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  /* we have to differentiate between int and float formats */
+  mimetype = gst_structure_get_name (structure);
+
+  if (!strncmp (mimetype, "audio/x-raw-int", 15)) {
+    gint endianness;
+
+    spec->type = GST_BUFTYPE_LINEAR;
+
+    /* extract the needed information from the cap */
+    if (!(gst_structure_get_int (structure, "width", &spec->width) &&
+            gst_structure_get_int (structure, "depth", &spec->depth) &&
+            gst_structure_get_boolean (structure, "signed", &spec->sign)))
+      goto parse_error;
+
+    /* extract endianness if needed */
+    if (spec->width > 8) {
+      if (!gst_structure_get_int (structure, "endianness", &endianness))
+        goto parse_error;
+    } else {
+      endianness = G_BYTE_ORDER;
+    }
+
+    spec->bigend = endianness == G_LITTLE_ENDIAN ? FALSE : TRUE;
+
+    spec->format =
+        build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1,
+        spec->bigend ? 1 : 0);
+  } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) {
+
+    spec->type = GST_BUFTYPE_FLOAT;
+
+    /* get layout */
+    if (!gst_structure_get_int (structure, "width", &spec->width))
+      goto parse_error;
+
+    /* match layout to format wrt to endianness */
+    switch (spec->width) {
+      case 32:
+        spec->format =
+            G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT32_LE : GST_FLOAT32_BE;
+        break;
+      case 64:
+        spec->format =
+            G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT64_LE : GST_FLOAT64_BE;
+        break;
+      default:
+        goto parse_error;
+    }
+  } else if (!strncmp (mimetype, "audio/x-alaw", 12)) {
+    spec->type = GST_BUFTYPE_A_LAW;
+    spec->format = GST_A_LAW;
+  } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) {
+    spec->type = GST_BUFTYPE_MU_LAW;
+    spec->format = GST_MU_LAW;
+  } else {
+    goto parse_error;
+  }
+
+  /* get rate and channels */
+  if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
+          gst_structure_get_int (structure, "channels", &spec->channels)))
+    goto parse_error;
+
+  spec->bytes_per_sample = (spec->width >> 3) * spec->channels;
+
+  gst_caps_replace (&spec->caps, caps);
+
+  g_return_val_if_fail (spec->latency_time != 0, FALSE);
+
+  /* calculate suggested segsize and segtotal */
+  spec->segsize =
+      spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
+  spec->segtotal = spec->buffer_time / spec->latency_time;
+
+  gst_ringbuffer_debug_spec_caps (spec);
+  gst_ringbuffer_debug_spec_buff (spec);
+
+  return TRUE;
+
+  /* ERRORS */
+parse_error:
+  {
+    GST_DEBUG ("could not parse caps");
+    return FALSE;
+  }
+}
+
 /**
  * gst_ringbuffer_set_callback:
  * @buf: the #GstRingBuffer to set the callback on
@@ -264,17 +462,17 @@ gst_ringbuffer_is_acquired (GstRingBuffer * buf)
 
 
 /**
- * gst_ringbuffer_play:
- * @buf: the #GstRingBuffer to play
+ * gst_ringbuffer_start:
+ * @buf: the #GstRingBuffer to start
  *
- * Start playing samples from the ringbuffer.
+ * Start processing samples from the ringbuffer.
  *
  * Returns: TRUE if the device could be started, FALSE on error.
  *
  * MT safe.
  */
 gboolean
-gst_ringbuffer_play (GstRingBuffer * buf)
+gst_ringbuffer_start (GstRingBuffer * buf)
 {
   gboolean res = FALSE;
   GstRingBufferClass *rclass;
@@ -283,16 +481,16 @@ gst_ringbuffer_play (GstRingBuffer * buf)
   g_return_val_if_fail (buf != NULL, FALSE);
 
   GST_LOCK (buf);
-  /* if paused, set to playing */
+  /* if stopped, set to started */
   res = g_atomic_int_compare_and_exchange (&buf->state,
-      GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PLAYING);
+      GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_STARTED);
 
   if (!res) {
     /* was not stopped, try from paused */
     res = g_atomic_int_compare_and_exchange (&buf->state,
-        GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_PLAYING);
+        GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_STARTED);
     if (!res) {
-      /* was not paused either, must be playing then */
+      /* was not paused either, must be started then */
       res = TRUE;
       goto done;
     }
@@ -304,8 +502,8 @@ gst_ringbuffer_play (GstRingBuffer * buf)
     if (rclass->resume)
       res = rclass->resume (buf);
   } else {
-    if (rclass->play)
-      res = rclass->play (buf);
+    if (rclass->start)
+      res = rclass->start (buf);
   }
 
   if (!res) {
@@ -322,7 +520,7 @@ done:
  * gst_ringbuffer_pause:
  * @buf: the #GstRingBuffer to pause
  *
- * Pause playing samples from the ringbuffer.
+ * Pause processing samples from the ringbuffer.
  *
  * Returns: TRUE if the device could be paused, FALSE on error.
  *
@@ -337,12 +535,12 @@ gst_ringbuffer_pause (GstRingBuffer * buf)
   g_return_val_if_fail (buf != NULL, FALSE);
 
   GST_LOCK (buf);
-  /* if playing, set to paused */
+  /* if started, set to paused */
   res = g_atomic_int_compare_and_exchange (&buf->state,
-      GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_PAUSED);
+      GST_RINGBUFFER_STATE_STARTED, GST_RINGBUFFER_STATE_PAUSED);
 
   if (!res) {
-    /* was not playing */
+    /* was not started */
     res = TRUE;
     goto done;
   }
@@ -355,7 +553,7 @@ gst_ringbuffer_pause (GstRingBuffer * buf)
     res = rclass->pause (buf);
 
   if (!res) {
-    buf->state = GST_RINGBUFFER_STATE_PLAYING;
+    buf->state = GST_RINGBUFFER_STATE_STARTED;
   }
 done:
   GST_UNLOCK (buf);
@@ -367,7 +565,7 @@ done:
  * gst_ringbuffer_stop:
  * @buf: the #GstRingBuffer to stop
  *
- * Stop playing samples from the ringbuffer.
+ * Stop processing samples from the ringbuffer.
  *
  * Returns: TRUE if the device could be stopped, FALSE on error.
  *
@@ -382,12 +580,12 @@ gst_ringbuffer_stop (GstRingBuffer * buf)
   g_return_val_if_fail (buf != NULL, FALSE);
 
   GST_LOCK (buf);
-  /* if playing, set to stopped */
+  /* if started, set to stopped */
   res = g_atomic_int_compare_and_exchange (&buf->state,
-      GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_STOPPED);
+      GST_RINGBUFFER_STATE_STARTED, GST_RINGBUFFER_STATE_STOPPED);
 
   if (!res) {
-    /* was not playing, must be stopped then */
+    /* was not started, must be stopped then */
     res = TRUE;
     goto done;
   }
@@ -400,7 +598,7 @@ gst_ringbuffer_stop (GstRingBuffer * buf)
     res = rclass->stop (buf);
 
   if (!res) {
-    buf->state = GST_RINGBUFFER_STATE_PLAYING;
+    buf->state = GST_RINGBUFFER_STATE_STARTED;
   } else {
     gst_ringbuffer_set_sample (buf, 0);
   }
@@ -442,38 +640,38 @@ gst_ringbuffer_delay (GstRingBuffer * buf)
 }
 
 /**
- * gst_ringbuffer_played_samples:
+ * gst_ringbuffer_samples_done:
  * @buf: the #GstRingBuffer to query
  *
- * Get the number of samples that were played by the ringbuffer
+ * Get the number of samples that were processed by the ringbuffer
  * since it was last started.
  *
- * Returns: The number of samples played by the ringbuffer.
+ * Returns: The number of samples processed by the ringbuffer.
  *
  * MT safe.
  */
 guint64
-gst_ringbuffer_played_samples (GstRingBuffer * buf)
+gst_ringbuffer_samples_done (GstRingBuffer * buf)
 {
-  gint segplayed;
+  gint segdone;
   guint64 raw, samples;
   guint delay;
 
   g_return_val_if_fail (buf != NULL, 0);
 
-  /* get the amount of segments we played */
-  segplayed = g_atomic_int_get (&buf->segplayed);
+  /* get the amount of segments we processed */
+  segdone = g_atomic_int_get (&buf->segdone);
 
-  /* and the number of samples not yet played */
+  /* and the number of samples not yet processed */
   delay = gst_ringbuffer_delay (buf);
 
-  samples = (segplayed * buf->samples_per_seg);
+  samples = (segdone * buf->samples_per_seg);
   raw = samples;
 
   if (samples >= delay)
     samples -= delay;
 
-  GST_DEBUG ("played samples: raw %llu, delay %u, real %llu", raw, delay,
+  GST_DEBUG ("processed samples: raw %llu, delay %u, real %llu", raw, delay,
       samples);
 
   return samples;
@@ -505,47 +703,47 @@ gst_ringbuffer_set_sample (GstRingBuffer * buf, guint64 sample)
 
   /* FIXME, we assume the ringbuffer can restart at a random 
    * position, round down to the beginning and keep track of
-   * offset when calculating the played samples. */
-  buf->segplayed = sample / buf->samples_per_seg;
+   * offset when calculating the processed samples. */
+  buf->segdone = sample / buf->samples_per_seg;
   buf->next_sample = sample;
 
   for (i = 0; i < buf->spec.segtotal; i++) {
     gst_ringbuffer_clear (buf, i);
   }
 
-  GST_DEBUG ("setting sample to %llu, segplayed %d", sample, buf->segplayed);
+  GST_DEBUG ("setting sample to %llu, segdone %d", sample, buf->segdone);
 }
 
 static gboolean
 wait_segment (GstRingBuffer * buf)
 {
-  /* buffer must be playing now or we deadlock since nobody is reading */
-  if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) {
-    GST_DEBUG ("play!");
-    gst_ringbuffer_play (buf);
+  /* buffer must be started now or we deadlock since nobody is reading */
+  if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED) {
+    GST_DEBUG ("start!");
+    gst_ringbuffer_start (buf);
   }
 
   /* take lock first, then update our waiting flag */
   GST_LOCK (buf);
   if (g_atomic_int_compare_and_exchange (&buf->waiting, 0, 1)) {
     GST_DEBUG ("waiting..");
-    if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
-      goto not_playing;
+    if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED)
+      goto not_started;
 
     GST_RINGBUFFER_WAIT (buf);
 
-    if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
-      goto not_playing;
+    if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED)
+      goto not_started;
   }
   GST_UNLOCK (buf);
 
   return TRUE;
 
   /* ERROR */
-not_playing:
+not_started:
   {
     GST_UNLOCK (buf);
-    GST_DEBUG ("stopped playing");
+    GST_DEBUG ("stopped processing");
     return FALSE;
   }
 }
@@ -573,7 +771,7 @@ guint
 gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
     guint len)
 {
-  gint segplayed;
+  gint segdone;
   gint segsize, segtotal, bps, sps;
   guint8 *dest;
 
@@ -582,7 +780,7 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
   g_return_val_if_fail (data != NULL, -1);
 
   if (sample == -1) {
-    /* play aligned with last sample */
+    /* process aligned with last sample */
     sample = buf->next_sample;
   } else {
     if (sample != buf->next_sample) {
@@ -614,17 +812,17 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
     while (TRUE) {
       gint diff;
 
-      /* get the currently playing segment */
-      segplayed = g_atomic_int_get (&buf->segplayed);
+      /* get the currently processed segment */
+      segdone = g_atomic_int_get (&buf->segdone);
 
       /* see how far away it is from the write segment */
-      diff = writeseg - segplayed;
+      diff = writeseg - segdone;
 
       GST_DEBUG
           ("pointer at %d, sample %llu, write to %d-%d, len %d, diff %d, segtotal %d, segsize %d",
-          segplayed, sample, writeseg, writeoff, len, diff, segtotal, segsize);
+          segdone, sample, writeseg, writeoff, len, diff, segtotal, segsize);
 
-      /* play segment too far ahead, we need to drop */
+      /* segment too far ahead, we need to drop */
       if (diff < 0) {
         /* we need to drop one segment at a time, pretend we wrote a
          * segment. */
@@ -639,7 +837,7 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
 
       /* else we need to wait for the segment to become writable. */
       if (!wait_segment (buf))
-        goto not_playing;
+        goto not_started;
     }
 
     /* we can write now */
@@ -660,9 +858,127 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
   return len;
 
   /* ERRORS */
-not_playing:
+not_started:
+  {
+    GST_DEBUG ("stopped processing");
+    return -1;
+  }
+}
+
+/**
+ * gst_ringbuffer_read:
+ * @buf: the #GstRingBuffer to read from
+ * @sample: the sample position of the data
+ * @data: where the data should be read
+ * @len: the length of the data to read
+ *
+ * Read @length samples from the ringbuffer into the memory pointed 
+ * to by @data.
+ * The first sample should be read from position @sample in
+ * the ringbuffer.
+ *
+ * @len should not be a multiple of the segment size of the ringbuffer
+ * although it is recommended.
+ *
+ * Returns: The number of samples read from the ringbuffer or -1 on
+ * error.
+ *
+ * MT safe.
+ */
+guint
+gst_ringbuffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
+    guint len)
+{
+  gint segdone;
+  gint segsize, segtotal, bps, sps;
+  guint8 *dest;
+
+  g_return_val_if_fail (buf != NULL, -1);
+  g_return_val_if_fail (buf->data != NULL, -1);
+  g_return_val_if_fail (data != NULL, -1);
+
+  if (sample == -1) {
+    /* process aligned with last sample */
+    sample = buf->next_sample;
+  } else {
+    if (sample != buf->next_sample) {
+      GST_WARNING ("discontinuity found got %" G_GUINT64_FORMAT
+          ", expected %" G_GUINT64_FORMAT, sample, buf->next_sample);
+    }
+  }
+
+  dest = GST_BUFFER_DATA (buf->data);
+  segsize = buf->spec.segsize;
+  segtotal = buf->spec.segtotal;
+  bps = buf->spec.bytes_per_sample;
+  sps = buf->samples_per_seg;
+
+  /* we assume the complete buffer will be consumed and the next sample
+   * should be written after this */
+  buf->next_sample = sample + len / bps;
+
+  /* read enough bytes */
+  while (len > 0) {
+    gint readlen;
+    gint readseg, readoff;
+
+    /* figure out the segment and the offset inside the segment where
+     * the sample should be written. */
+    readseg = sample / sps;
+    readoff = (sample % sps) * bps;
+
+    while (TRUE) {
+      gint diff;
+
+      /* get the currently processed segment */
+      segdone = g_atomic_int_get (&buf->segdone);
+
+      /* see how far away it is from the read segment */
+      diff = segdone - readseg;
+
+      GST_DEBUG
+          ("pointer at %d, sample %llu, read from %d-%d, len %d, diff %d, segtotal %d, segsize %d",
+          segdone, sample, readseg, readoff, len, diff, segtotal, segsize);
+
+      /* segment too far ahead, we need to drop */
+      if (diff < 0) {
+        /* we need to drop one segment at a time, pretend we wrote a
+         * segment. */
+        readlen = MIN (segsize, len);
+        goto next;
+      }
+
+      /* read segment is within readable range, we can break the loop and
+       * start reading the data. */
+      if (diff > 0 && diff < segtotal)
+        break;
+
+      /* else we need to wait for the segment to become readable. */
+      if (!wait_segment (buf))
+        goto not_started;
+    }
+
+    /* we can read now */
+    readseg = readseg % segtotal;
+    readlen = MIN (segsize - readoff, len);
+
+    GST_DEBUG ("read @%p seg %d, off %d, len %d",
+        dest + readseg * segsize, readseg, readoff, readlen);
+
+    memcpy (data, dest + readseg * segsize + readoff, readlen);
+
+  next:
+    len -= readlen;
+    data += readlen;
+    sample += readlen / bps;
+  }
+
+  return len;
+
+  /* ERRORS */
+not_started:
   {
-    GST_DEBUG ("stopped playing");
+    GST_DEBUG ("stopped processing");
     return -1;
   }
 }
@@ -677,7 +993,7 @@ not_playing:
  * Returns a pointer to memory where the data from segment @segment
  * can be found. This function is used by subclasses.
  *
- * Returns: FALSE if the buffer is not playing.
+ * Returns: FALSE if the buffer is not started.
  *
  * MT safe.
  */
@@ -686,23 +1002,24 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment,
     guint8 ** readptr, gint * len)
 {
   guint8 *data;
-  gint segplayed;
+  gint segdone;
 
-  /* buffer must be playing */
-  if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
+  /* buffer must be started */
+  if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED)
     return FALSE;
 
   g_return_val_if_fail (buf != NULL, FALSE);
   g_return_val_if_fail (buf->data != NULL, FALSE);
+  g_return_val_if_fail (segment != NULL, FALSE);
   g_return_val_if_fail (readptr != NULL, FALSE);
   g_return_val_if_fail (len != NULL, FALSE);
 
   data = GST_BUFFER_DATA (buf->data);
 
-  /* get the position of the play pointer */
-  segplayed = g_atomic_int_get (&buf->segplayed);
+  /* get the position of the pointer */
+  segdone = g_atomic_int_get (&buf->segdone);
 
-  *segment = segplayed % buf->spec.segtotal;
+  *segment = segdone % buf->spec.segtotal;
   *len = buf->spec.segsize;
   *readptr = data + *segment * *len;
 
@@ -711,7 +1028,7 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment,
     buf->callback (buf, *readptr, *len, buf->cb_data);
 
   GST_DEBUG ("prepare read from segment %d (real %d) @%p",
-      *segment, segplayed, *readptr);
+      *segment, segdone, *readptr);
 
   return TRUE;
 }
@@ -722,7 +1039,7 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment,
  * @advance: the number of segments written
  *
  * Subclasses should call this function to notify the fact that 
- * @advance segments are now played by the device.
+ * @advance segments are now processed by the device.
  *
  * MT safe.
  */
@@ -732,7 +1049,7 @@ gst_ringbuffer_advance (GstRingBuffer * buf, guint advance)
   g_return_if_fail (buf != NULL);
 
   /* update counter */
-  g_atomic_int_add (&buf->segplayed, advance);
+  g_atomic_int_add (&buf->segdone, advance);
 
   /* the lock is already taken when the waiting flag is set,
    * we grab the lock as well to make sure the waiter is actually
index 38079c5..3f478f2 100644 (file)
@@ -44,7 +44,7 @@ typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint8* data, guint
 typedef enum {
   GST_RINGBUFFER_STATE_STOPPED,
   GST_RINGBUFFER_STATE_PAUSED,
-  GST_RINGBUFFER_STATE_PLAYING,
+  GST_RINGBUFFER_STATE_STARTED,
 } GstRingBufferState;
 
 typedef enum {
@@ -160,11 +160,11 @@ struct _GstRingBuffer {
 
   /*< public >*/ /* ATOMIC */
   gint                  state;         /* state of the buffer */
-  gint                  segplayed;     /* number of segments played since last start */
+  gint                  segdone;       /* number of segments processed since last start */
   gint                  waiting;       /* when waiting for a segment to be freed */
 
   /*< private >*/
-  guint64               next_sample;   /* the next sample we need to write */
+  guint64               next_sample;   /* the next sample we need to process */
   GstRingBufferCallback  callback;
   gpointer               cb_data;
 };
@@ -179,7 +179,7 @@ struct _GstRingBufferClass {
   gboolean     (*release)      (GstRingBuffer *buf);
 
   /* playback control */
-  gboolean     (*play)         (GstRingBuffer *buf);
+  gboolean     (*start)        (GstRingBuffer *buf);
   gboolean     (*pause)        (GstRingBuffer *buf);
   gboolean     (*resume)       (GstRingBuffer *buf);
   gboolean     (*stop)         (GstRingBuffer *buf);
@@ -194,6 +194,10 @@ GType gst_ringbuffer_get_type(void);
 void           gst_ringbuffer_set_callback     (GstRingBuffer *buf, GstRingBufferCallback cb, 
                                                 gpointer user_data);
 
+gboolean       gst_ringbuffer_parse_caps       (GstRingBufferSpec *spec, GstCaps *caps);
+void           gst_ringbuffer_debug_spec_caps  (GstRingBufferSpec *spec);
+void           gst_ringbuffer_debug_spec_buff  (GstRingBufferSpec *spec);
+
 /* allocate resources */
 gboolean       gst_ringbuffer_acquire          (GstRingBuffer *buf, GstRingBufferSpec *spec);
 gboolean       gst_ringbuffer_release          (GstRingBuffer *buf);
@@ -201,21 +205,25 @@ gboolean  gst_ringbuffer_release          (GstRingBuffer *buf);
 gboolean       gst_ringbuffer_is_acquired      (GstRingBuffer *buf);
 
 /* playback/pause */
-gboolean       gst_ringbuffer_play             (GstRingBuffer *buf);
+gboolean       gst_ringbuffer_start            (GstRingBuffer *buf);
 gboolean       gst_ringbuffer_pause            (GstRingBuffer *buf);
 gboolean       gst_ringbuffer_stop             (GstRingBuffer *buf);
 
 /* get status */
 guint          gst_ringbuffer_delay            (GstRingBuffer *buf);
-guint64                gst_ringbuffer_played_samples   (GstRingBuffer *buf);
+guint64                gst_ringbuffer_samples_done     (GstRingBuffer *buf);
 
 void           gst_ringbuffer_set_sample       (GstRingBuffer *buf, guint64 sample);
 
 /* commit samples */
 guint          gst_ringbuffer_commit           (GstRingBuffer *buf, guint64 sample, 
                                                 guchar *data, guint len);
+/* read samples */
+guint          gst_ringbuffer_read             (GstRingBuffer *buf, guint64 sample, 
+                                                guchar *data, guint len);
 
 /* mostly protected */
+gboolean       gst_ringbuffer_prepare_write    (GstRingBuffer *buf, gint *segment, guint8 **writeptr, gint *len);
 gboolean       gst_ringbuffer_prepare_read     (GstRingBuffer *buf, gint *segment, guint8 **readptr, gint *len);
 void           gst_ringbuffer_clear            (GstRingBuffer *buf, gint segment);
 void           gst_ringbuffer_advance          (GstRingBuffer *buf, guint advance);