An attempt at a set of audio base classes together with some design docs.
authorWim Taymans <wim.taymans@gmail.com>
Wed, 20 Apr 2005 10:19:54 +0000 (10:19 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 20 Apr 2005 10:19:54 +0000 (10:19 +0000)
Original commit message from CVS:
* docs/design-audiosinks.txt:
* gst-libs/gst/audio/Makefile.am:
* gst-libs/gst/audio/TODO:
* gst-libs/gst/audio/gstaudiosink.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_play), (gst_audioringbuffer_stop),
(gst_audioringbuffer_delay), (gst_audiosink_base_init),
(gst_audiosink_class_init), (gst_audiosink_init),
(gst_audiosink_create_ringbuffer):
* gst-libs/gst/audio/gstaudiosink.h:
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_baseaudiosink_base_init), (gst_baseaudiosink_class_init),
(gst_baseaudiosink_init), (gst_baseaudiosink_set_property),
(gst_baseaudiosink_get_property), (gst_baseaudiosink_setcaps),
(gst_baseaudiosink_get_times), (gst_baseaudiosink_event),
(gst_baseaudiosink_preroll), (gst_baseaudiosink_render),
(gst_baseaudiosink_create_ringbuffer),
(gst_baseaudiosink_callback), (gst_baseaudiosink_change_state):
* gst-libs/gst/audio/gstbaseaudiosink.h:
* gst-libs/gst/audio/gstringbuffer.c: (gst_ringbuffer_get_type),
(gst_ringbuffer_class_init), (gst_ringbuffer_init),
(gst_ringbuffer_dispose), (gst_ringbuffer_finalize),
(gst_ringbuffer_set_callback), (gst_ringbuffer_acquire),
(gst_ringbuffer_release), (gst_ringbuffer_play_unlocked),
(gst_ringbuffer_play), (gst_ringbuffer_pause),
(gst_ringbuffer_resume), (gst_ringbuffer_stop),
(gst_ringbuffer_callback), (gst_ringbuffer_delay),
(gst_ringbuffer_played_samples), (gst_ringbuffer_commit),
(gst_ringbuffer_prepare_read), (gst_ringbuffer_clear):
* gst-libs/gst/audio/gstringbuffer.h:
An attempt at a set of audio base classes together with some
design docs.

ChangeLog
docs/design-audiosinks.txt [new file with mode: 0644]
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/TODO [new file with mode: 0644]
gst-libs/gst/audio/gstaudiosink.c [new file with mode: 0644]
gst-libs/gst/audio/gstaudiosink.h [new file with mode: 0644]
gst-libs/gst/audio/gstbaseaudiosink.c [new file with mode: 0644]
gst-libs/gst/audio/gstbaseaudiosink.h [new file with mode: 0644]
gst-libs/gst/audio/gstringbuffer.c [new file with mode: 0644]
gst-libs/gst/audio/gstringbuffer.h [new file with mode: 0644]

index 3deab71c70a36a4783023f2ea934af8d2930d466..8102d3485b6e2d9b23e7229932d06f770cfc918a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2005-04-20  Wim Taymans  <wim@fluendo.com>
+
+       * docs/design-audiosinks.txt:
+       * gst-libs/gst/audio/Makefile.am:
+       * gst-libs/gst/audio/TODO:
+       * gst-libs/gst/audio/gstaudiosink.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_play), (gst_audioringbuffer_stop),
+       (gst_audioringbuffer_delay), (gst_audiosink_base_init),
+       (gst_audiosink_class_init), (gst_audiosink_init),
+       (gst_audiosink_create_ringbuffer):
+       * gst-libs/gst/audio/gstaudiosink.h:
+       * gst-libs/gst/audio/gstbaseaudiosink.c:
+       (gst_baseaudiosink_base_init), (gst_baseaudiosink_class_init),
+       (gst_baseaudiosink_init), (gst_baseaudiosink_set_property),
+       (gst_baseaudiosink_get_property), (gst_baseaudiosink_setcaps),
+       (gst_baseaudiosink_get_times), (gst_baseaudiosink_event),
+       (gst_baseaudiosink_preroll), (gst_baseaudiosink_render),
+       (gst_baseaudiosink_create_ringbuffer),
+       (gst_baseaudiosink_callback), (gst_baseaudiosink_change_state):
+       * gst-libs/gst/audio/gstbaseaudiosink.h:
+       * gst-libs/gst/audio/gstringbuffer.c: (gst_ringbuffer_get_type),
+       (gst_ringbuffer_class_init), (gst_ringbuffer_init),
+       (gst_ringbuffer_dispose), (gst_ringbuffer_finalize),
+       (gst_ringbuffer_set_callback), (gst_ringbuffer_acquire),
+       (gst_ringbuffer_release), (gst_ringbuffer_play_unlocked),
+       (gst_ringbuffer_play), (gst_ringbuffer_pause),
+       (gst_ringbuffer_resume), (gst_ringbuffer_stop),
+       (gst_ringbuffer_callback), (gst_ringbuffer_delay),
+       (gst_ringbuffer_played_samples), (gst_ringbuffer_commit),
+       (gst_ringbuffer_prepare_read), (gst_ringbuffer_clear):
+       * gst-libs/gst/audio/gstringbuffer.h:
+       An attempt at a set of audio base classes together with some
+       design docs.
+
 2005-04-20  Wim Taymans  <wim@fluendo.com>
 
        * gst/audioconvert/Makefile.am:
diff --git a/docs/design-audiosinks.txt b/docs/design-audiosinks.txt
new file mode 100644 (file)
index 0000000..bc66c6d
--- /dev/null
@@ -0,0 +1,137 @@
+Audiosink design
+----------------
+
+Requirements:
+
+ - must operate chain based.
+   Most simple playback pipelines will push audio from the decoders
+   into the audio sink.
+ - must operate getrange based
+   Most professional audio applications will operate in a mode where
+   the audio sink pulls samples from the pipeline. This is typically
+   done in a callback from the audiosink requesting N samples. The
+   callback is either scheduled from a thread or from an interrupt
+   from the audio hardware device. 
+
+ - Exact sample accurate clocks.
+   the audiosink must be able to provide a clock that is sample 
+   accurate even if samples are dropped or when discontinuities are
+   found in the stream.
+
+ - Exact timing of playback.
+   The audiosink must be able to play samples at their exact times.
+
+ - use DMA access when possible.
+   When the hardware can do DMA we should use it. This should also
+   work over bufferpools to avoid data copying to/from kernel space.
+
+
+Design:
+
+ The design is based on a set of base classes and the concept of a
+ ringbuffer of samples.
+
+   +-----------+   - provide preroll, rendering, timing
+   + basesink  +   - caps nego
+   +-----+-----+
+         |
+   +-----V----------+   - manages ringbuffer
+   + baseaudiosink  +   - manages scheduling (push/pull)
+   +-----+----------+   - manages clock/query/seek
+         |              - manages scheduling of samples in the ringbuffer
+         |              - manages caps parsing
+         |
+   +-----V------+   - default ringbuffer implementation with a GThread
+   + audiosink  +   - subclasses provide open/read/close methods
+   +------------+
+
+  The ringbuffer is a contiguous piece of memory divided into segtotal
+  pieces of segments. Each segment has segsize bytes.
+
+         play position                    write position
+           v                                 v
+   +---+---+---+-------------------------------------+----------+
+   + 0 | 1 | 2 | ....                                | segtotal |
+   +---+---+---+-------------------------------------+----------+
+   <--->
+     segsize bytes = N samples * bytes_per_sample.
+
+  
+  The ringbuffer has a play and write position, which is expressed in
+  segments. The play position is where the device is currently reading
+  samples and the write position is where new samples can be written
+  into the buffer.
+
+  The latency of the ringbuffer is the distance between the play and
+  write position. The lowest latency is the size of a segment, thus
+  smaller segment sizes allow for lower latency. 
+
+  The ringbuffer can be put to the PLAYING or STOPPED state. 
+  
+  In the STOPPED state no samples are played to the device and the play
+  pointer does not advance. 
+  
+  In the PLAYING state samples are written to the device and the ringbuffer 
+  should call a configurable callback after each segment is written to the
+  device. In this state the play pointer is advanced after each segment is
+  written.
+
+  A write operation to the ringbuffer will put new samples in the ringbuffer.
+  If there is not enough space in the ringbuffer, the write operation will 
+  block.  The playback of the buffer never stops, even if the buffer is 
+  empty. When the buffer is empty, silence is played by the device.
+
+  The ringbuffer is implemented with lockfree atomic operations, especially
+  on the reading side so that low-latency operations are possible.
+
+
+Scheduling:
+
+  - chain based mode:
+
+   In chain based mode, bytes are written into the ringbuffer. This operation
+   will eventually block when the ringbuffer is filled. 
+  
+   When no samples arrive in time, the ringbuffer will play silence. Each 
+   buffer that arrives will be placed into the ringbuffer at the correct 
+   times. This means that dropping samples or inserting silence is done
+   automatically and very accurate and independend of the play pointer.
+   
+   In this mode, the ringbuffer is usually kept as full as possible. When 
+   using a small buffer (small segsize and segtotal), the latency for audio 
+   to start from the sink to when it is played can be kept low but at least
+   one context switch has to be made between read and write.
+
+  - getrange based mode
+
+    In getrange based mode, the baseaudiosink will use the callback function
+    of the ringbuffer to get a segsize samples from the peer element. These
+    samples will then be placed in the ringbuffer at the next play position.
+    It is assumed that the getrange function returns fast enough to fill the
+    ringbuffer before the play pointer reaches the write pointer.
+  
+    In this mode, the ringbuffer is usually kept as empty as possible. There
+    is no context switch needed between the elements that create the samples
+    and the actual writing of the samples to the device.
+
+
+DMA mode:
+
+  - Elements that can do DMA based access to the audio device have to subclass
+    from the GstBaseAudioSink class and wrap the DMA ringbuffer in a subclass
+    of GstRingBuffer.
+    
+    The ringbuffer subclass should trigger a callback after writing or playing
+    each sample to the device. This callback can be triggered from a thread or
+    from a signal from the audio device. 
+
+
+Clocks:
+
+   The GstBaseAudioSink class will use the ringbuffer to act as a clock provider.
+   It can do this by using the play pointer and the delay to calculate the
+   clock time.
+  
+
+  
index 54208713fd51124d3ad0a594f76fd514121264b5..58d9c219fb1dcb1c8ef7c57c437e31ef1c788786 100644 (file)
@@ -17,7 +17,10 @@ CLEANFILES = gstaudiofilterexample.c \
        $(BUILT_SOURCES)
 
 libgstaudio_la_SOURCES = audio.c audioclock.c \
-       multichannel.c
+       multichannel.c \
+       gstaudiosink.c \
+       gstbaseaudiosink.c \
+       gstringbuffer.c
 nodist_libgstaudio_la_SOURCES = $(built_sources)
 
 libgstaudioincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
@@ -25,14 +28,15 @@ libgstaudioinclude_HEADERS = \
        audio.h \
        audioclock.h \
        gstaudiofilter.h \
-       multichannel.h
-
-nodist_libgstaudioinclude_HEADERS = \
+       gstaudiosink.h \
+       gstbaseaudiosink.h \
+       gstringbuffer.h \
+       multichannel.h \
        multichannel-enumtypes.h
 
 libgstaudio_la_LIBADD =
 libgstaudio_la_CFLAGS = $(GST_CFLAGS)
-libgstaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS)
 
 libgstaudiofilter_la_SOURCES = gstaudiofilter.c gstaudiofilter.h
 libgstaudiofilter_la_CFLAGS = $(GST_CFLAGS)
@@ -45,4 +49,9 @@ libgstaudiofilterexample_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 gstaudiofilterexample.c: $(srcdir)/make_filter $(srcdir)/gstaudiofiltertemplate.c
        $(srcdir)/make_filter AudiofilterExample $(srcdir)/gstaudiofiltertemplate.c
 
+noinst_PROGRAMS = testchannels
+testchannels_SOURCES = testchannels.c
+testchannels_CFLAGS = $(GST_CFLAGS)
+testchannels_LDFLAGS = $(GST_LIBS)
+
 include $(top_srcdir)/common/glib-gen.mak
diff --git a/gst-libs/gst/audio/TODO b/gst-libs/gst/audio/TODO
new file mode 100644 (file)
index 0000000..c349258
--- /dev/null
@@ -0,0 +1,13 @@
+TODO 
+----
+
+- audio base classes:
+   - GstBaseAudioSink
+      - parse caps into rinbuffer spec, also mase sure surround sound
+        is parsed correctly.
+      - implement seek/query/convert
+      - implement clocks
+      - implement getrange scheduling
+   - GstRingBuffer
+      - copy samples to right position in ringbuffer
+      
diff --git a/gst-libs/gst/audio/gstaudiosink.c b/gst-libs/gst/audio/gstaudiosink.c
new file mode 100644 (file)
index 0000000..0394718
--- /dev/null
@@ -0,0 +1,404 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstaudiosink.c: simple audio sink 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 "gstaudiosink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_audiosink_debug);
+#define GST_CAT_DEFAULT gst_audiosink_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_play (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf);
+static guint gst_audioringbuffer_delay (GstRingBuffer * buf);
+
+/* ringbuffer abstract base class */
+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, "GstAudioRingBuffer",
+        &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->play = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
+  gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop);
+
+  gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay);
+}
+
+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.
+ */
+static void
+audioringbuffer_thread_func (GstRingBuffer * buf)
+{
+  GstAudioSink *sink;
+  GstAudioSinkClass *csink;
+  GstAudioRingBuffer *abuf = GST_AUDIORINGBUFFER (buf);
+  WriteFunc writefunc;
+  gint segsize, segtotal;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+  csink = GST_AUDIOSINK_GET_CLASS (sink);
+
+  GST_DEBUG ("enter thread");
+
+  writefunc = csink->write;
+  if (writefunc == NULL)
+    goto no_function;
+
+  segsize = buf->spec.segsize;
+  segtotal = buf->spec.segtotal;
+
+  while (TRUE) {
+    if (g_atomic_int_get (&buf->state) == GST_RINGBUFFER_STATE_PLAYING) {
+      gint to_write, written;
+      guint8 *readptr;
+      gint readseg;
+
+      /* we write one segment */
+      to_write = segsize;
+      written = 0;
+      /* need to read and write the next segment */
+      readseg = (buf->playseg + 1) % segtotal;
+      /* get a pointer in the buffer to this segment */
+      readptr = gst_ringbuffer_prepare_read (buf, readseg);
+
+      do {
+        written = writefunc (sink, readptr + written, to_write);
+        if (written < 0 || written > to_write) {
+          perror ("error writing data\n");
+          break;
+        }
+        to_write -= written;
+      } while (to_write > 0);
+
+      /* clear written samples */
+      gst_ringbuffer_clear (buf, readseg);
+
+      /* we wrote one segment */
+      gst_ringbuffer_callback (buf, 1);
+    } else {
+      GST_LOCK (abuf);
+      GST_DEBUG ("signal wait");
+      GST_AUDIORINGBUFFER_SIGNAL (buf);
+      GST_DEBUG ("wait for play");
+      GST_AUDIORINGBUFFER_WAIT (buf);
+      GST_DEBUG ("got signal");
+      if (!abuf->running) {
+        GST_UNLOCK (abuf);
+        GST_DEBUG ("stop running");
+        goto done;
+      }
+      GST_UNLOCK (abuf);
+    }
+  }
+done:
+  GST_DEBUG ("exit thread");
+
+  return;
+
+  /* ERROR */
+no_function:
+  {
+    GST_DEBUG ("no write function, exit thread");
+    return;
+  }
+}
+
+static void
+gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer)
+{
+  ringbuffer->running = TRUE;
+  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)
+{
+  GstAudioSink *sink;
+  GstAudioSinkClass *csink;
+  gboolean result = FALSE;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+  csink = GST_AUDIOSINK_GET_CLASS (sink);
+
+  if (csink->open)
+    result = csink->open (sink, 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), 0, GST_BUFFER_SIZE (buf));
+
+  sink->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)
+{
+  GstAudioSink *sink;
+  GstAudioSinkClass *csink;
+  GstAudioRingBuffer *abuf;
+  gboolean result = FALSE;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+  csink = GST_AUDIOSINK_GET_CLASS (sink);
+  abuf = GST_AUDIORINGBUFFER (buf);
+
+  abuf->running = FALSE;
+  GST_AUDIORINGBUFFER_SIGNAL (buf);
+  GST_UNLOCK (buf);
+
+  /* join the thread */
+  g_thread_join (sink->thread);
+
+  GST_LOCK (buf);
+
+  if (csink->close)
+    result = csink->close (sink);
+
+  return result;
+}
+
+static gboolean
+gst_audioringbuffer_play (GstRingBuffer * buf)
+{
+  GstAudioSink *sink;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+
+  GST_DEBUG ("play");
+  GST_AUDIORINGBUFFER_SIGNAL (buf);
+
+  return TRUE;
+}
+
+static gboolean
+gst_audioringbuffer_stop (GstRingBuffer * buf)
+{
+  GstAudioSink *sink;
+  GstAudioSinkClass *csink;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+  csink = GST_AUDIOSINK_GET_CLASS (sink);
+
+  /* unblock any pending writes to the audio device */
+  if (csink->reset)
+    csink->reset (sink);
+
+  GST_DEBUG ("stop");
+  GST_AUDIORINGBUFFER_WAIT (buf);
+
+  return TRUE;
+}
+
+static guint
+gst_audioringbuffer_delay (GstRingBuffer * buf)
+{
+  GstAudioSink *sink;
+  GstAudioSinkClass *csink;
+  guint res = 0;
+
+  sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
+  csink = GST_AUDIOSINK_GET_CLASS (sink);
+
+  if (csink->delay)
+    res = csink->delay (sink);
+
+  return res;
+}
+
+/* AudioSink signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+};
+
+#define _do_init(bla) \
+    GST_DEBUG_CATEGORY_INIT (gst_audiosink_debug, "audiosink", 0, "audiosink element");
+
+GST_BOILERPLATE_FULL (GstAudioSink, gst_audiosink, GstBaseAudioSink,
+    GST_TYPE_BASEAUDIOSINK, _do_init);
+
+static GstRingBuffer *gst_audiosink_create_ringbuffer (GstBaseAudioSink * sink);
+
+static void
+gst_audiosink_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_audiosink_class_init (GstAudioSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSinkClass *gstbasesink_class;
+  GstBaseAudioSinkClass *gstbaseaudiosink_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+  gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
+
+  gstbaseaudiosink_class->create_ringbuffer =
+      GST_DEBUG_FUNCPTR (gst_audiosink_create_ringbuffer);
+}
+
+static void
+gst_audiosink_init (GstAudioSink * audiosink)
+{
+}
+
+static GstRingBuffer *
+gst_audiosink_create_ringbuffer (GstBaseAudioSink * sink)
+{
+  GstRingBuffer *buffer;
+
+  buffer = g_object_new (GST_TYPE_AUDIORINGBUFFER, NULL);
+
+  return buffer;
+}
diff --git a/gst-libs/gst/audio/gstaudiosink.h b/gst-libs/gst/audio/gstaudiosink.h
new file mode 100644 (file)
index 0000000..fba15d6
--- /dev/null
@@ -0,0 +1,85 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstaudiosink.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 sinks.
+ *
+ * This base class only requires subclasses to implement a set
+ * of simple functions.
+ *
+ * - open: open the device with the specified caps
+ * - write: write the samples to the audio device
+ * - close: close the device
+ * - delay: the number of samples queued in the device
+ * - reset: unblock a write to the device and reset.
+ *
+ * All scheduling of samples and timestamps is done in this
+ * base class together with the GstBaseAudioSink using a 
+ * default implementation of a ringbuffer that uses threads.
+ */
+
+#ifndef __GST_AUDIOSINK_H__
+#define __GST_AUDIOSINK_H__
+
+#include <gst/gst.h>
+#include "gstbaseaudiosink.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AUDIOSINK             (gst_audiosink_get_type())
+#define GST_AUDIOSINK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOSINK,GstAudioSink))
+#define GST_AUDIOSINK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOSINK,GstAudioSinkClass))
+#define GST_AUDIOSINK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIOSINK,GstAudioSinkClass))
+#define GST_IS_AUDIOSINK(obj)                  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOSINK))
+#define GST_IS_AUDIOSINK_CLASS(obj)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOSINK))
+
+typedef struct _GstAudioSink GstAudioSink;
+typedef struct _GstAudioSinkClass GstAudioSinkClass;
+
+struct _GstAudioSink {
+  GstBaseAudioSink      element;
+
+  /*< private >*/ /* with LOCK */
+  GThread   *thread;
+};
+
+struct _GstAudioSinkClass {
+  GstBaseAudioSinkClass parent_class;
+
+  /* vtable */
+
+  /* open the device with given specs */
+  gboolean (*open)   (GstAudioSink *sink, GstRingBufferSpec *spec);
+  /* close the device */
+  gboolean (*close)  (GstAudioSink *sink);
+  /* write samples to the device */
+  guint    (*write)  (GstAudioSink *sink, gpointer data, guint length);
+  /* get number of samples queued in the device */
+  guint    (*delay)  (GstAudioSink *sink);
+  /* reset the audio device, unblock from a write */
+  void     (*reset)  (GstAudioSink *sink);
+};
+
+GType gst_audiosink_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_AUDIOSINK_H__ */
diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c
new file mode 100644 (file)
index 0000000..4d77608
--- /dev/null
@@ -0,0 +1,267 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbaseaudiosink.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 "gstbaseaudiosink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_baseaudiosink_debug);
+#define GST_CAT_DEFAULT gst_baseaudiosink_debug
+
+/* BaseAudioSink signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+#define DEFAULT_BUFFER -1
+#define DEFAULT_LATENCY        -1
+enum
+{
+  PROP_0,
+  PROP_BUFFER,
+  PROP_LATENCY,
+};
+
+#define _do_init(bla) \
+    GST_DEBUG_CATEGORY_INIT (gst_baseaudiosink_debug, "baseaudiosink", 0, "baseaudiosink element");
+
+GST_BOILERPLATE_FULL (GstBaseAudioSink, gst_baseaudiosink, GstBaseSink,
+    GST_TYPE_BASESINK, _do_init);
+
+static void gst_baseaudiosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_baseaudiosink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static GstElementStateReturn gst_baseaudiosink_change_state (GstElement *
+    element);
+
+static GstFlowReturn gst_baseaudiosink_preroll (GstBaseSink * bsink,
+    GstBuffer * buffer);
+static GstFlowReturn gst_baseaudiosink_render (GstBaseSink * bsink,
+    GstBuffer * buffer);
+static void gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event);
+static void gst_baseaudiosink_get_times (GstBaseSink * bsink,
+    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
+static gboolean gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps);
+
+//static guint gst_baseaudiosink_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gst_baseaudiosink_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_baseaudiosink_class_init (GstBaseAudioSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSinkClass *gstbasesink_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosink_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosink_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER,
+      g_param_spec_uint64 ("buffer", "Buffer",
+          "Size of audio buffer in nanoseconds (-1 = default)",
+          0, G_MAXUINT64, DEFAULT_BUFFER, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY,
+      g_param_spec_uint64 ("latency", "Latency",
+          "Audio latency in nanoseconds (-1 = default)",
+          0, G_MAXUINT64, DEFAULT_LATENCY, G_PARAM_READWRITE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosink_change_state);
+
+  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_baseaudiosink_event);
+  gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_baseaudiosink_preroll);
+  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_baseaudiosink_render);
+  gstbasesink_class->get_times =
+      GST_DEBUG_FUNCPTR (gst_baseaudiosink_get_times);
+  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_baseaudiosink_setcaps);
+}
+
+static void
+gst_baseaudiosink_init (GstBaseAudioSink * baseaudiosink)
+{
+  baseaudiosink->buffer = DEFAULT_BUFFER;
+  baseaudiosink->latency = DEFAULT_LATENCY;
+}
+
+static void
+gst_baseaudiosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstBaseAudioSink *sink;
+
+  sink = GST_BASEAUDIOSINK (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER:
+      break;
+    case PROP_LATENCY:
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstBaseAudioSink *sink;
+
+  sink = GST_BASEAUDIOSINK (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER:
+      break;
+    case PROP_LATENCY:
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
+{
+  GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
+  GstRingBufferSpec *spec;
+
+  spec = &sink->ringbuffer->spec;
+
+  gst_caps_replace (&spec->caps, caps);
+  spec->buffersize = sink->buffer;
+  spec->latency = sink->latency;
+
+  spec->segtotal = 0x7fff;
+  spec->segsize = 0x2048;
+
+  gst_ringbuffer_release (sink->ringbuffer);
+  gst_ringbuffer_acquire (sink->ringbuffer, spec);
+
+  return TRUE;
+}
+
+static void
+gst_baseaudiosink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+    GstClockTime * start, GstClockTime * end)
+{
+  *start = GST_CLOCK_TIME_NONE;
+  *end = GST_CLOCK_TIME_NONE;
+}
+
+static void
+gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event)
+{
+}
+
+static GstFlowReturn
+gst_baseaudiosink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
+{
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_baseaudiosink_render (GstBaseSink * bsink, GstBuffer * buf)
+{
+  GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
+
+  gst_ringbuffer_commit (sink->ringbuffer, 0,
+      GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+
+  return GST_FLOW_OK;
+}
+
+GstRingBuffer *
+gst_baseaudiosink_create_ringbuffer (GstBaseAudioSink * sink)
+{
+  GstBaseAudioSinkClass *bclass;
+  GstRingBuffer *buffer = NULL;
+
+  bclass = GST_BASEAUDIOSINK_GET_CLASS (sink);
+  if (bclass->create_ringbuffer)
+    buffer = bclass->create_ringbuffer (sink);
+
+  if (buffer) {
+    gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (sink));
+  }
+
+  return buffer;
+}
+
+void
+gst_baseaudiosink_callback (GstRingBuffer * rbuf, guint advance, gpointer data)
+{
+  //GstBaseAudioSink *sink = GST_BASEAUDIOSINK (data);
+}
+
+static GstElementStateReturn
+gst_baseaudiosink_change_state (GstElement * element)
+{
+  GstElementStateReturn ret = GST_STATE_SUCCESS;
+  GstBaseAudioSink *sink = GST_BASEAUDIOSINK (element);
+  GstElementState transition = GST_STATE_TRANSITION (element);
+
+  switch (transition) {
+    case GST_STATE_NULL_TO_READY:
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      sink->ringbuffer = gst_baseaudiosink_create_ringbuffer (sink);
+      gst_ringbuffer_set_callback (sink->ringbuffer, gst_baseaudiosink_callback,
+          sink);
+      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_stop (sink->ringbuffer);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      gst_ringbuffer_release (sink->ringbuffer);
+      gst_object_unref (GST_OBJECT (sink->ringbuffer));
+      break;
+    case GST_STATE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
diff --git a/gst-libs/gst/audio/gstbaseaudiosink.h b/gst-libs/gst/audio/gstbaseaudiosink.h
new file mode 100644 (file)
index 0000000..1c72055
--- /dev/null
@@ -0,0 +1,95 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbaseaudiosink.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 sinks.
+ *
+ * It uses a ringbuffer to schedule playback of samples. This makes
+ * it very easy to drop or insert samples to align incomming 
+ * buffers to the exact playback timestamp.
+ *
+ * Subclasses must provide a ringbuffer pointing to either DMA
+ * memory or regular memory. A subclass should also call a callback
+ * function when it has played N segments in the buffer. The subclass
+ * is free to use a thread to signal this callback, use EIO or any
+ * other mechanism.
+ *
+ * The base class is able to operate in push or pull mode. The chain
+ * mode will queue the samples in the ringbuffer as much as possible.
+ * The available space is calculated in the callback function.
+ *
+ * The pull mode will pull_range() a new buffer of N samples with a
+ * configurable latency. This allows for high-end real time 
+ * audio processing pipelines driven by the audiosink. The callback
+ * function will be used to perform a pull_range() on the sinkpad.
+ * The thread scheduling the callback can be a real-time thread.
+ *
+ * Subclasses must implement a GstRingBuffer in addition to overriding
+ * the methods in GstBaseSink and this class.
+ */
+
+#ifndef __GST_BASEAUDIOSINK_H__
+#define __GST_BASEAUDIOSINK_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+#include "gstringbuffer.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_BASEAUDIOSINK          (gst_baseaudiosink_get_type())
+#define GST_BASEAUDIOSINK(obj)                  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASEAUDIOSINK,GstBaseAudioSink))
+#define GST_BASEAUDIOSINK_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASEAUDIOSINK,GstBaseAudioSinkClass))
+#define GST_BASEAUDIOSINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASEAUDIOSINK, GstBaseAudioSinkClass))
+#define GST_IS_BASEAUDIOSINK(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASEAUDIOSINK))
+#define GST_IS_BASEAUDIOSINK_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASEAUDIOSINK))
+
+#define GST_BASEAUDIOSINK_CLOCK(obj)    (GST_BASEAUDIOSINK (obj)->clock)
+#define GST_BASEAUDIOSINK_PAD(obj)      (GST_BASEAUDIOSINK (obj)->sinkpad)
+
+typedef struct _GstBaseAudioSink GstBaseAudioSink;
+typedef struct _GstBaseAudioSinkClass GstBaseAudioSinkClass;
+
+struct _GstBaseAudioSink {
+  GstBaseSink   element;
+
+  /* our ringbuffer */
+  GstRingBuffer *ringbuffer;
+
+  /* required buffer and latency */
+  GstClockTime   buffer;
+  GstClockTime   latency;
+};
+
+struct _GstBaseAudioSinkClass {
+  GstBaseSinkClass parent_class;
+
+  /* subclass ringbuffer allocation */
+  GstRingBuffer* (*create_ringbuffer)  (GstBaseAudioSink *sink);
+};
+
+GType gst_baseaudiosink_get_type(void);
+
+GstRingBuffer *gst_baseaudiosink_create_ringbuffer (GstBaseAudioSink *sink);
+
+G_END_DECLS
+
+#endif /* __GST_BASEAUDIOSINK_H__ */
diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c
new file mode 100644 (file)
index 0000000..959e2e7
--- /dev/null
@@ -0,0 +1,656 @@
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstringbuffer.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 "gstringbuffer.h"
+
+static void gst_ringbuffer_class_init (GstRingBufferClass * klass);
+static void gst_ringbuffer_init (GstRingBuffer * ringbuffer);
+static void gst_ringbuffer_dispose (GObject * object);
+static void gst_ringbuffer_finalize (GObject * object);
+
+static GstObjectClass *parent_class = NULL;
+
+/* ringbuffer abstract base class */
+GType
+gst_ringbuffer_get_type (void)
+{
+  static GType ringbuffer_type = 0;
+
+  if (!ringbuffer_type) {
+    static const GTypeInfo ringbuffer_info = {
+      sizeof (GstRingBufferClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_ringbuffer_class_init,
+      NULL,
+      NULL,
+      sizeof (GstRingBuffer),
+      0,
+      (GInstanceInitFunc) gst_ringbuffer_init,
+      NULL
+    };
+
+    ringbuffer_type = g_type_register_static (GST_TYPE_OBJECT, "GstRingBuffer",
+        &ringbuffer_info, G_TYPE_FLAG_ABSTRACT);
+  }
+  return ringbuffer_type;
+}
+
+static void
+gst_ringbuffer_class_init (GstRingBufferClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstObjectClass *gstobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstobject_class = (GstObjectClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ringbuffer_dispose);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ringbuffer_finalize);
+}
+
+static void
+gst_ringbuffer_init (GstRingBuffer * ringbuffer)
+{
+  ringbuffer->acquired = FALSE;
+  ringbuffer->state = GST_RINGBUFFER_STATE_STOPPED;
+  ringbuffer->playseg = -1;
+  ringbuffer->writeseg = -1;
+  ringbuffer->segfilled = 0;
+  ringbuffer->freeseg = -1;
+  ringbuffer->segplayed = 0;
+  ringbuffer->cond = g_cond_new ();
+}
+
+static void
+gst_ringbuffer_dispose (GObject * object)
+{
+  GstRingBuffer *ringbuffer = GST_RINGBUFFER (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (ringbuffer));
+}
+
+static void
+gst_ringbuffer_finalize (GObject * object)
+{
+  GstRingBuffer *ringbuffer = GST_RINGBUFFER (object);
+
+  g_cond_free (ringbuffer->cond);
+
+  G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer));
+}
+
+/**
+ * gst_ringbuffer_set_callback:
+ * @buf: the #GstRingBuffer to set the callback on
+ * @cb: the callback to set
+ * @data: use data passed to the callback
+ *
+ * Sets the given callback function on the buffer. This function
+ * will be called every time a segment has been written to a device.
+ *
+ * MT safe.
+ */
+void
+gst_ringbuffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb,
+    gpointer data)
+{
+  GST_LOCK (buf);
+  buf->callback = cb;
+  buf->cb_data = data;
+  GST_UNLOCK (buf);
+}
+
+/**
+ * gst_ringbuffer_acquire:
+ * @buf: the #GstRingBuffer to acquire
+ * @spec: the specs of the buffer
+ *
+ * Allocate the resources for the ringbuffer. This function fills
+ * in the data pointer of the ring buffer with a valid #GstBuffer
+ * to which samples can be written.
+ *
+ * Returns: TRUE if the device could be acquired, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  GST_LOCK (buf);
+  if (buf->acquired) {
+    res = TRUE;
+    goto done;
+  }
+  buf->acquired = TRUE;
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->acquire)
+    res = rclass->acquire (buf, spec);
+
+  if (!res) {
+    buf->acquired = FALSE;
+  } else {
+    buf->freeseg = spec->segtotal;
+    if (buf->spec.bytes_per_sample != 0) {
+      buf->samples_per_seg = buf->spec.segsize / buf->spec.bytes_per_sample;
+    } else {
+      g_warning ("invalid bytes_per_sample from acquire ringbuffer");
+      buf->samples_per_seg = buf->spec.segsize;
+    }
+  }
+done:
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+/**
+ * gst_ringbuffer_release:
+ * @buf: the #GstRingBuffer to release
+ *
+ * Free the resources of the ringbuffer.
+ *
+ * Returns: TRUE if the device could be released, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_release (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  gst_ringbuffer_stop (buf);
+
+  GST_LOCK (buf);
+  if (!buf->acquired) {
+    res = TRUE;
+    goto done;
+  }
+  buf->acquired = FALSE;
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->release)
+    res = rclass->release (buf);
+
+  if (!res) {
+    buf->acquired = TRUE;
+  }
+
+done:
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+static gboolean
+gst_ringbuffer_play_unlocked (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  /* if paused, set to playing */
+  res = g_atomic_int_compare_and_exchange (&buf->state,
+      GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PLAYING);
+
+  if (!res) {
+    /* was not stopped */
+    res = TRUE;
+    goto done;
+  }
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->play)
+    res = rclass->play (buf);
+
+  if (!res) {
+    buf->state = GST_RINGBUFFER_STATE_PAUSED;
+  }
+done:
+  return res;
+}
+
+/**
+ * gst_ringbuffer_play:
+ * @buf: the #GstRingBuffer to play
+ *
+ * Start playing samples from the ringbuffer.
+ *
+ * Returns: TRUE if the device could be started, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_play (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+
+  GST_LOCK (buf);
+  res = gst_ringbuffer_play_unlocked (buf);
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+/**
+ * gst_ringbuffer_pause:
+ * @buf: the #GstRingBuffer to pause
+ *
+ * Pause playing samples from the ringbuffer.
+ *
+ * Returns: TRUE if the device could be paused, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_pause (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  GST_LOCK (buf);
+  /* if playing, set to paused */
+  res = g_atomic_int_compare_and_exchange (&buf->state,
+      GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_PAUSED);
+
+  if (!res) {
+    /* was not playing */
+    res = TRUE;
+    goto done;
+  }
+
+  /* signal any waiters */
+  GST_RINGBUFFER_SIGNAL (buf);
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->pause)
+    res = rclass->pause (buf);
+
+  if (!res) {
+    buf->state = GST_RINGBUFFER_STATE_PLAYING;
+  }
+done:
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+/**
+ * gst_ringbuffer_resume:
+ * @buf: the #GstRingBuffer to resume
+ *
+ * Resume playing samples from the ringbuffer in the paused state.
+ *
+ * Returns: TRUE if the device could be paused, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_resume (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  GST_LOCK (buf);
+  /* if playing, set to paused */
+  res = g_atomic_int_compare_and_exchange (&buf->state,
+      GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_PLAYING);
+
+  if (!res) {
+    /* was not paused */
+    res = TRUE;
+    goto done;
+  }
+
+  /* signal any waiters */
+  GST_RINGBUFFER_SIGNAL (buf);
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->resume)
+    res = rclass->resume (buf);
+
+  if (!res) {
+    buf->state = GST_RINGBUFFER_STATE_PAUSED;
+  }
+done:
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+
+/**
+ * gst_ringbuffer_stop:
+ * @buf: the #GstRingBuffer to stop
+ *
+ * Stop playing samples from the ringbuffer.
+ *
+ * Returns: TRUE if the device could be stopped, FALSE on error.
+ *
+ * MT safe.
+ */
+gboolean
+gst_ringbuffer_stop (GstRingBuffer * buf)
+{
+  gboolean res = FALSE;
+  GstRingBufferClass *rclass;
+
+  GST_LOCK (buf);
+  /* if playing, set to stopped */
+  res = g_atomic_int_compare_and_exchange (&buf->state,
+      GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_STOPPED);
+
+  if (!res) {
+    /* was not playing, must be stopped then */
+    res = TRUE;
+    goto done;
+  }
+
+  /* signal any waiters */
+  GST_RINGBUFFER_SIGNAL (buf);
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->stop)
+    res = rclass->stop (buf);
+
+  if (!res) {
+    buf->state = GST_RINGBUFFER_STATE_PLAYING;
+  } else {
+    buf->segfilled = 0;
+    buf->playseg = -1;
+    buf->writeseg = -1;
+    buf->freeseg = buf->spec.segtotal;
+    buf->segplayed = 0;
+  }
+done:
+  GST_UNLOCK (buf);
+
+  return res;
+}
+
+/**
+ * gst_ringbuffer_callback:
+ * @buf: the #GstRingBuffer to callback
+ * @advance: the number of segments written
+ *
+ * Subclasses should call this function to notify the fact that 
+ * @advance segments are now played by the device.
+ *
+ * MT safe.
+ */
+void
+gst_ringbuffer_callback (GstRingBuffer * buf, guint advance)
+{
+  gint prevfree;
+  gint segtotal;
+
+  if (advance == 0)
+    return;
+
+  segtotal = buf->spec.segtotal;
+
+  /* update counter */
+  g_atomic_int_add (&buf->segplayed, advance);
+
+  /* update free segments counter */
+  prevfree = g_atomic_int_exchange_and_add (&buf->freeseg, advance);
+  if (prevfree + advance > segtotal) {
+    g_warning ("underrun!! read %d, write %d, advance %d, free %d, prevfree %d",
+        buf->playseg, buf->writeseg, advance, buf->freeseg, prevfree);
+    buf->freeseg = segtotal;
+    buf->writeseg = buf->playseg;
+    /* make sure to signal */
+    prevfree = -1;
+  }
+
+  buf->playseg = (buf->playseg + advance) % segtotal;
+
+  if (prevfree == -1) {
+    /* we need to take the lock to make sure the other thread is
+     * blocking in the wait */
+    GST_LOCK (buf);
+    GST_RINGBUFFER_SIGNAL (buf);
+    GST_UNLOCK (buf);
+  }
+
+  if (buf->callback)
+    buf->callback (buf, advance, buf->cb_data);
+}
+
+/**
+ * gst_ringbuffer_delay:
+ * @buf: the #GstRingBuffer to query
+ *
+ * Get the number of samples queued in the audio device. This is
+ * usually less than the segment size but can be bigger when the
+ * implementation uses another internal buffer between the audio
+ * device.
+ *
+ * Returns: The number of samples queued in the audio device.
+ *
+ * MT safe.
+ */
+guint
+gst_ringbuffer_delay (GstRingBuffer * buf)
+{
+  GstRingBufferClass *rclass;
+  guint res = 0;
+
+  rclass = GST_RINGBUFFER_GET_CLASS (buf);
+  if (rclass->delay)
+    res = rclass->delay (buf);
+
+  return res;
+}
+
+/**
+ * gst_ringbuffer_played_samples:
+ * @buf: the #GstRingBuffer to query
+ *
+ * Get the number of samples that were played by the ringbuffer
+ * since it was last started.
+ *
+ * Returns: The number of samples played by the ringbuffer.
+ *
+ * MT safe.
+ */
+guint64
+gst_ringbuffer_played_samples (GstRingBuffer * buf)
+{
+  gint segplayed;
+  guint64 samples;
+  guint delay;
+
+  /* get the amount of segments we played */
+  segplayed = g_atomic_int_get (&buf->segplayed);
+  /* and the number of samples not yet played */
+  delay = gst_ringbuffer_delay (buf);
+
+  samples = (segplayed * buf->samples_per_seg) - delay;
+
+  return samples;
+}
+
+/**
+ * gst_ringbuffer_commit:
+ * @buf: the #GstRingBuffer to commit
+ * @sample: the sample position of the data
+ * @data: the data to commit
+ * @len: the length of the data to commit
+ *
+ * Commit @length samples pointed to by @data to the ringbuffer
+ * @buf. The first sample should be written at 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 written to the ringbuffer.
+ *
+ * MT safe.
+ */
+/* FIXME, write the samples into the right position in the ringbuffer based
+ * on the sample position argument 
+ */
+guint
+gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
+    guint len)
+{
+  guint towrite = len;
+  gint segsize, segtotal;
+  guint8 *dest;
+
+  if (buf->data == NULL)
+    goto no_buffer;
+
+  dest = GST_BUFFER_DATA (buf->data);
+  segsize = buf->spec.segsize;
+  segtotal = buf->spec.segtotal;
+
+  /* we write the complete buffer in chunks of segsize so that we can check for
+   * a filled buffer after each segment. */
+  while (towrite > 0) {
+    gint segavail;
+    gint segwrite;
+    gint writeseg;
+    gint segfilled;
+
+    segfilled = buf->segfilled;
+
+    /* check for partial buffer */
+    if (G_LIKELY (segfilled == 0)) {
+      gint prevfree;
+      gint newseg;
+
+      /* no partial buffer to fill up, allocate a new one */
+      prevfree = g_atomic_int_exchange_and_add (&buf->freeseg, -1);
+      if (prevfree == 0) {
+        /* nothing was free */
+        GST_DEBUG ("filled %d %d", buf->writeseg, buf->playseg);
+
+        GST_LOCK (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_ringbuffer_play_unlocked (buf);
+
+        GST_RINGBUFFER_WAIT (buf);
+        if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
+          goto not_playing;
+        GST_UNLOCK (buf);
+      }
+
+      /* need to do this atomic as the reader updates the write pointer on
+       * overruns */
+      do {
+        writeseg = g_atomic_int_get (&buf->writeseg);
+        newseg = (writeseg + 1) % segtotal;
+      } while (!g_atomic_int_compare_and_exchange (&buf->writeseg, writeseg,
+              newseg));
+      writeseg = newseg;
+    } else {
+      /* this is the segment we should write to */
+      writeseg = g_atomic_int_get (&buf->writeseg);
+    }
+    if (writeseg < 0 || writeseg > segtotal) {
+      g_warning ("invalid segment %d", writeseg);
+      writeseg = 0;
+    }
+
+    /* this is the available size now in the current segment */
+    segavail = segsize - segfilled;
+
+    /* we write up to the available space */
+    segwrite = MIN (segavail, towrite);
+
+    memcpy (dest + writeseg * segsize + segfilled, data, segwrite);
+
+    towrite -= segwrite;
+    data += segwrite;
+
+    if (segfilled + segwrite == segsize) {
+      buf->segfilled = 0;
+    } else {
+      buf->segfilled = segfilled + segwrite;
+    }
+  }
+  return len - towrite;
+
+no_buffer:
+  {
+    GST_DEBUG ("no buffer");
+    return -1;
+  }
+not_playing:
+  {
+    GST_UNLOCK (buf);
+    GST_DEBUG ("stopped playing");
+    return len - towrite;
+  }
+}
+
+/**
+ * gst_ringbuffer_prepare_read:
+ * @buf: the #GstRingBuffer to read from
+ * @segment: the segment to read
+ *
+ * Returns a pointer to memory where the data from segment @segment
+ * can be found.
+ *
+ * MT safe.
+ */
+guint8 *
+gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint segment)
+{
+  guint8 *data;
+
+  data = GST_BUFFER_DATA (buf->data);
+
+  return data + (segment % buf->spec.segtotal) * buf->spec.segsize;
+}
+
+/**
+ * gst_ringbuffer_clear:
+ * @buf: the #GstRingBuffer to clear
+ * @segment: the segment to clear
+ *
+ * Clear the given segment of the buffer with silence samples.
+ * This function is used by subclasses.
+ *
+ * MT safe.
+ */
+void
+gst_ringbuffer_clear (GstRingBuffer * buf, gint segment)
+{
+  guint8 *data;
+
+  data = GST_BUFFER_DATA (buf->data);
+
+  memset (data + (segment % buf->spec.segtotal) * buf->spec.segsize, 0,
+      buf->spec.segsize);
+}
diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h
new file mode 100644 (file)
index 0000000..6d5f5f5
--- /dev/null
@@ -0,0 +1,176 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstringbuffer.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.
+ */
+
+#ifndef __GST_RINGBUFFER_H__
+#define __GST_RINGBUFFER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RINGBUFFER             (gst_ringbuffer_get_type())
+#define GST_RINGBUFFER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RINGBUFFER,GstRingBuffer))
+#define GST_RINGBUFFER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RINGBUFFER,GstRingBufferClass))
+#define GST_RINGBUFFER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RINGBUFFER, GstRingBufferClass))
+#define GST_IS_RINGBUFFER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RINGBUFFER))
+#define GST_IS_RINGBUFFER_CLASS(obj)     (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RINGBUFFER))
+
+typedef struct _GstRingBuffer GstRingBuffer;
+typedef struct _GstRingBufferClass GstRingBufferClass;
+typedef struct _GstRingBufferSpec GstRingBufferSpec;
+
+typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint advance, gpointer data);
+
+typedef enum {
+  GST_RINGBUFFER_STATE_STOPPED,
+  GST_RINGBUFFER_STATE_PAUSED,
+  GST_RINGBUFFER_STATE_PLAYING,
+} GstRingBufferState;
+
+typedef enum {
+  GST_SEGSTATE_INVALID,
+  GST_SEGSTATE_EMPTY,
+  GST_SEGSTATE_FILLED,
+  GST_SEGSTATE_PARTIAL,
+} GstRingBufferSegState;
+
+typedef enum
+{
+  GST_U8,
+  GST_S8,
+
+  GST_U16_LE,
+  GST_S16_LE,
+  GST_U16_BE,
+  GST_S16_BE,
+
+  GST_U24_LE,
+  GST_S24_LE,
+  GST_U24_BE,
+  GST_S24_BE,
+
+  GST_FLOAT_LE,
+  GST_FLOAT_BE,
+} GstBufferFormat;
+
+struct _GstRingBufferSpec
+{
+  /* in */
+  GstCaps  *caps;              /* the caps of the buffer */
+
+  /* in/out */
+  GstBufferFormat format;
+  gint      rate;
+  gint      channels;
+  
+  GstClockTime latency;                /* the required/actual latency */
+  GstClockTime buffersize;     /* the required/actual size of the buffer */
+  gint     segsize;            /* size of one buffer segement */
+  gint     segtotal;           /* total number of segments */
+
+  /* out */
+  gint     bytes_per_sample;   /* number of bytes of one sample */
+  guint8   silence_sample[32];  /* bytes representing silence */
+};
+
+#define GST_RINGBUFFER_GET_COND(buf) (((GstRingBuffer *)buf)->cond)
+#define GST_RINGBUFFER_WAIT(buf)     (g_cond_wait (GST_RINGBUFFER_GET_COND (buf), GST_GET_LOCK (buf)))
+#define GST_RINGBUFFER_SIGNAL(buf)   (g_cond_signal (GST_RINGBUFFER_GET_COND (buf)))
+#define GST_RINGBUFFER_BROADCAST(buf)(g_cond_broadcast (GST_RINGBUFFER_GET_COND (buf)))
+
+struct _GstRingBuffer {
+  GstObject             object;
+
+  /*< public >*/ /* with LOCK */
+  GCond                 *cond;
+  gboolean               acquired;
+  GstBuffer             *data;
+  GstRingBufferSpec      spec;
+  GstRingBufferSegState *segstate;
+  gint                  samples_per_seg;     /* number of samples per segment */
+
+  /*< public >*/ /* ATOMIC */
+  gint                  state;         /* state of the buffer */
+  gint                  freeseg;       /* number of free segments */
+  gint                  segplayed;     /* number of segments played since last start */
+
+  /*< protected >*/
+  gint                   playseg;      /* segment currently playing */
+  gint                   writeseg;     /* segment currently written */
+  gint                   segfilled;    /* bytes used in current write segment */
+
+  /*< private >*/
+  GstRingBufferCallback  callback;
+  gpointer               cb_data;
+};
+
+struct _GstRingBufferClass {
+  GstObjectClass parent_class;
+
+  /*< public >*/
+  /* allocate the resources for the ringbuffer using the given specs */
+  gboolean     (*acquire)      (GstRingBuffer *buf, GstRingBufferSpec *spec);
+  /* free resources of the ringbuffer */
+  gboolean     (*release)      (GstRingBuffer *buf);
+
+  /* playback control */
+  gboolean     (*play)         (GstRingBuffer *buf);
+  gboolean     (*pause)        (GstRingBuffer *buf);
+  gboolean     (*resume)       (GstRingBuffer *buf);
+  gboolean     (*stop)         (GstRingBuffer *buf);
+
+  /* number of samples queued in device */
+  guint        (*delay)        (GstRingBuffer *buf);
+};
+
+GType gst_ringbuffer_get_type(void);
+
+/* callback stuff */
+void           gst_ringbuffer_set_callback     (GstRingBuffer *buf, GstRingBufferCallback cb, 
+                                                gpointer data);
+void           gst_ringbuffer_callback         (GstRingBuffer *buf, guint advance);
+
+/* allocate resources */
+gboolean       gst_ringbuffer_acquire          (GstRingBuffer *buf, GstRingBufferSpec *spec);
+gboolean       gst_ringbuffer_release          (GstRingBuffer *buf);
+
+/* playback/pause */
+gboolean       gst_ringbuffer_play             (GstRingBuffer *buf);
+gboolean       gst_ringbuffer_pause            (GstRingBuffer *buf);
+gboolean       gst_ringbuffer_resume           (GstRingBuffer *buf);
+gboolean       gst_ringbuffer_stop             (GstRingBuffer *buf);
+
+/* get status */
+guint          gst_ringbuffer_delay            (GstRingBuffer *buf);
+guint64                gst_ringbuffer_played_samples   (GstRingBuffer *buf);
+
+/* commit samples */
+guint          gst_ringbuffer_commit           (GstRingBuffer *buf, guint64 sample, 
+                                                guchar *data, guint len);
+
+/* mostly protected */
+guint8*                gst_ringbuffer_prepare_read     (GstRingBuffer *buf, gint segment);
+void           gst_ringbuffer_clear            (GstRingBuffer *buf, gint segment);
+
+G_END_DECLS
+
+#endif /* __GST_RINGBUFFER_H__ */