changeset 3162:e387614b9be9

alsa-ng: Import rewritten ALSA plugin. This is still woefully incomplete, but supports basic playback. This driver uses the "safe" ALSA API subset, including use of blocking I/O. Right now, it is hardcoded to use "default". Do not complain about bugs in this plugin.
author William Pitcock <nenolod@atheme.org>
date Thu, 14 May 2009 21:05:11 -0500
parents 6dd886b5c72b
children 26a2c237ef53
files src/alsa-ng/Makefile src/alsa-ng/alsa-core.c src/alsa-ng/alsa-debug.h src/alsa-ng/alsa-ringbuffer.c src/alsa-ng/alsa-ringbuffer.h src/alsa-ng/alsa-stdinc.h src/alsa-ng/alsa-types.h src/alsa-ng/alsa-util.c
diffstat 8 files changed, 843 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/Makefile	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,14 @@
+PLUGIN = ALSA${PLUGIN_SUFFIX}
+
+SRCS = alsa-core.c \
+       alsa-ringbuffer.c \
+       alsa-util.c
+
+include ../../buildsys.mk
+include ../../extra.mk
+
+plugindir := ${plugindir}/${OUTPUT_PLUGIN_DIR}
+
+CFLAGS += ${PLUGIN_CFLAGS}
+CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS}  ${GTK_CFLAGS} ${GLIB_CFLAGS}  ${ALSA_CFLAGS} -I../..
+LIBS += ${GTK_LIBS} ${GLIB_LIBS}  ${ALSA_LIBS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-core.c	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,250 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#define ALSA_DEBUG
+#include "alsa-stdinc.h"
+
+static snd_pcm_t *pcm_handle = NULL;
+static alsaplug_ringbuf_t pcm_ringbuf;
+static gboolean pcm_going = FALSE;
+static GThread *audio_thread = NULL;
+static gint bps;
+static GMutex *pcm_mutex;
+static GCond *pcm_cond;
+
+static gsize wr_total = 0;
+static gsize wr_hwframes = 0;
+
+static gint flush_request;
+
+static void
+alsaplug_write_buffer(gpointer data, gint length)
+{
+    snd_pcm_sframes_t wr_frames;
+
+    while (length > 0)
+    {
+        gint frames = snd_pcm_bytes_to_frames(pcm_handle, length);
+        wr_frames = snd_pcm_writei(pcm_handle, data, frames);
+
+        if (wr_frames > 0)
+        {
+            gint written = snd_pcm_frames_to_bytes(pcm_handle, wr_frames);
+            length -= written;
+            data += written;
+        }
+        else
+        {
+            gint err = snd_pcm_recover(pcm_handle, wr_frames, 1);
+            if (err < 0)
+                _ERROR("(write) snd_pcm_recover: %s", snd_strerror(err));
+
+            return;
+        }
+    }
+}
+
+static gpointer
+alsaplug_loop(gpointer unused)
+{
+    gchar buf[2048];
+
+    while (pcm_going)
+    {
+        if (flush_request != -1)
+        {
+            snd_pcm_drop(pcm_handle);
+            snd_pcm_prepare(pcm_handle);
+            wr_total = flush_request * (bps / 1000);
+            flush_request = -1;
+        }
+
+        if (alsaplug_ringbuffer_read(&pcm_ringbuf, buf, 2048) == -1)
+        {
+            GTimeVal pcm_abs_time;
+
+            g_get_current_time(&pcm_abs_time);
+            g_time_val_add(&pcm_abs_time, 10000);
+
+            g_mutex_lock(pcm_mutex);
+            g_cond_timed_wait(pcm_cond, pcm_mutex, &pcm_abs_time);
+            g_mutex_unlock(pcm_mutex);
+
+            continue;
+        }
+
+        alsaplug_write_buffer(buf, 2048);
+    }
+
+    snd_pcm_close(pcm_handle);
+    pcm_handle = NULL;
+
+    return NULL;
+}
+
+/********************************************************************************
+ * Output Plugin API implementation.                                            *
+ ********************************************************************************/
+
+static OutputPluginInitStatus
+alsaplug_init(void)
+{
+    gint card = -1;
+
+    pcm_mutex = g_mutex_new();
+    pcm_cond = g_cond_new();
+
+    if (snd_card_next(&card) != 0)
+        return OUTPUT_PLUGIN_INIT_NO_DEVICES;
+
+    return OUTPUT_PLUGIN_INIT_FOUND_DEVICES;
+}
+
+static gint
+alsaplug_open_audio(AFormat fmt, gint rate, gint nch)
+{
+    gint err, bitwidth, ringbuf_size;
+    snd_pcm_format_t afmt;
+    snd_pcm_hw_params_t *hwparams = NULL;
+
+    afmt = alsaplug_format_convert(fmt);
+
+    if ((err = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
+    {
+        _ERROR("snd_pcm_open: %s", snd_strerror(err));
+        pcm_handle = NULL;
+        return -1;
+    }
+
+    snd_pcm_hw_params_alloca(&hwparams);
+    snd_pcm_hw_params_any(pcm_handle, hwparams);
+    snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+    snd_pcm_hw_params_set_format(pcm_handle, hwparams, afmt);
+    snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nch);
+    snd_pcm_hw_params_set_rate(pcm_handle, hwparams, rate, 0);
+
+    err = snd_pcm_hw_params(pcm_handle, hwparams);
+    if (err < 0)
+    {
+        _ERROR("snd_pcm_hw_params failed: %s", snd_strerror(err));
+        return -1;
+    }
+
+    bitwidth = snd_pcm_format_physical_width(afmt);
+    bps = (rate * bitwidth * nch) >> 3;
+    ringbuf_size = aud_cfg->output_buffer_size * bps / 1000;
+    alsaplug_ringbuffer_init(&pcm_ringbuf, ringbuf_size);
+    pcm_going = TRUE;
+    flush_request = -1;
+
+    audio_thread = g_thread_create(alsaplug_loop, NULL, TRUE, NULL);
+    return 1;
+}
+
+static void
+alsaplug_close_audio(void)
+{
+    pcm_going = FALSE;
+
+    g_thread_join(audio_thread);
+
+    wr_total = 0;
+    wr_hwframes = 0;
+    bps = 0;
+}
+
+static void
+alsaplug_write_audio(gpointer data, gint length)
+{
+    wr_total += length;
+    alsaplug_ringbuffer_write(&pcm_ringbuf, data, length);
+}
+
+static gint
+alsaplug_output_time(void)
+{
+    snd_pcm_sframes_t delay;
+    gsize bytes = wr_total;
+
+    if (pcm_going && pcm_handle != NULL)
+    {
+        if (!snd_pcm_delay(pcm_handle, &delay))
+        {
+            guint d = snd_pcm_frames_to_bytes(pcm_handle, delay);
+            if (bytes < d)
+                bytes = 0;
+            else
+                bytes -= d;
+        }
+
+        return (bytes * 1000) / bps;
+    }
+
+    return 0;
+}
+
+static gint
+alsaplug_written_time(void)
+{
+    if (pcm_going)
+        return (wr_total * 1000) / bps;
+
+    return 0;
+}
+
+static gint
+alsaplug_buffer_free(void)
+{
+    return alsaplug_ringbuffer_free(&pcm_ringbuf);
+}
+
+static void
+alsaplug_flush(gint time)
+{
+    flush_request = time;
+    while (flush_request != -1 && pcm_going)
+        g_usleep(10000);
+}
+
+static gint
+alsaplug_buffer_playing(void)
+{
+    return pcm_going;
+}
+
+/********************************************************************************
+ * Plugin glue.                                                                 *
+ ********************************************************************************/
+
+static OutputPlugin alsa_op = {
+    .description = "ALSA Output Plugin (-ng)",
+    .probe_priority = 1,
+    .init = alsaplug_init,
+    .open_audio = alsaplug_open_audio,
+    .close_audio = alsaplug_close_audio,
+    .write_audio = alsaplug_write_audio,
+    .output_time = alsaplug_output_time,
+    .written_time = alsaplug_written_time,
+    .buffer_free = alsaplug_buffer_free,
+    .buffer_playing = alsaplug_buffer_playing,
+    .flush = alsaplug_flush,
+};
+
+OutputPlugin *alsa_oplist[] = { &alsa_op, NULL };
+SIMPLE_OUTPUT_PLUGIN(alsa, alsa_oplist);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-debug.h	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2005 Ralf Ertzinger
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdio.h>
+
+#define _ENTER _DEBUG("enter")
+#define _LEAVE _DEBUG("leave"); return
+#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: ALSA: %s:%d (%s): " string "\n", \
+    tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0)
+
+#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__)
+
+#ifdef ALSA_DEBUG
+#define _DEBUG(...) _MESSAGE("DEBUG",  __VA_ARGS__)
+#else
+#define _DEBUG(...) {}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-ringbuffer.c	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,366 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Ringbuffer implementation
+ *
+ * GPL
+ */
+#include <string.h>
+#include "alsa-ringbuffer.h"
+#include "alsa-debug.h"
+
+#ifdef ALSAPLUG_RINGBUFFER_DEBUG
+/*
+ * An internal assertion function to make sure that the
+ * ringbuffer structure is consistient.
+ *
+ * WARNING: This function will call abort() if the ringbuffer
+ * is found to be inconsistient.
+ */
+static void _alsaplug_ringbuffer_assert(alsaplug_ringbuf_t* rb) {
+
+    unsigned int realused;
+
+    _ENTER;
+
+    _DEBUG("rb->buf=%p, rb->end=%p, rb->wp=%p, rb->rp=%p, rb->free=%u, rb->used=%u, rb->size=%u",
+            rb->buf, rb->end, rb->wp, rb->rp, rb->free, rb->used, rb->size);
+
+    if (0 == rb->size) {
+        _ERROR("Buffer size is 0");
+        abort();
+    }
+
+    if (NULL == rb->buf) {
+        _ERROR("Buffer start is NULL");
+        abort();
+    }
+
+    if (rb->used+rb->free != rb->size) {
+        _ERROR("rb->free and rb->used do not add up to rb->size");
+        abort();
+    }
+
+    if (rb->buf+(rb->size-1) != rb->end) {
+        _ERROR("rb->buf and rb->end not rb->size bytes apart");
+        abort();
+    }
+
+    if ((rb->wp < rb->buf) || (rb->wp > rb->end)) {
+        _ERROR("Write pointer outside buffer space");
+        abort();
+    }
+
+    if ((rb->rp < rb->buf) || (rb->rp > rb->end)) {
+        _ERROR("Read pointer outside buffer space");
+        abort();
+    }
+
+    if (rb->rp <= rb->wp) {
+        realused = rb->wp - rb->rp;
+    } else {
+        realused = (rb->end - rb->rp) + 1 + (rb->wp-rb->buf);
+    }
+
+    if (rb->used != realused) {
+        _ERROR("Usage count is inconsistient (is %d, should be %d)", rb->used, realused);
+        abort();
+    }
+
+    _LEAVE;
+}
+#endif
+
+/*
+ * Reset a ringbuffer structure (i.e. discard
+ * all data inside of it)
+ */
+void alsaplug_ringbuffer_reset(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+
+    rb->wp = rb->buf;
+    rb->rp = rb->buf;
+    rb->free = rb->size;
+    rb->used = 0;
+    rb->end = rb->buf+(rb->size-1);
+
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE;
+}
+
+/* 
+ * Initialize a ringbuffer structure (including
+ * memory allocation.
+ *
+ * Return -1 on error
+ */
+int alsaplug_ringbuffer_init(alsaplug_ringbuf_t* rb, unsigned int size) {
+
+    _ENTER;
+
+    if (0 == size) {
+        _LEAVE -1;
+    }
+
+    if (NULL == (rb->buf = malloc(size))) {
+        _LEAVE -1;
+    }
+    rb->size = size;
+
+    if (NULL == (rb->lock = g_mutex_new())) {
+        _LEAVE -1;
+    }
+
+    rb->_free_lock = 1;
+
+    alsaplug_ringbuffer_reset(rb);
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/* 
+ * Initialize a ringbuffer structure (including
+ * memory allocation.
+ * The mutex to be used is passed in the function call.
+ * The mutex must not be held while calling this function.
+ *
+ * Return -1 on error
+ */
+int alsaplug_ringbuffer_init_with_lock(alsaplug_ringbuf_t* rb, unsigned int size, alsaplug_ringbuffer_mutex_t* lock) {
+
+    _ENTER;
+
+    if (0 == size) {
+        _LEAVE -1;
+    }
+
+    rb->lock = lock;
+    rb->_free_lock = 0;
+
+    if (NULL == (rb->buf = malloc(size))) {
+        _LEAVE -1;
+    }
+    rb->size = size;
+    alsaplug_ringbuffer_reset(rb);
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Write size bytes at buf into the ringbuffer.
+ * Return -1 on error (not enough space in buffer)
+ */
+int alsaplug_ringbuffer_write(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int ret = -1;
+    int endfree;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+
+    ASSERT_RB(rb);
+
+    if (rb->free < size) {
+        ret = -1;
+        goto out;
+    }
+
+    endfree = (rb->end - rb->wp)+1;
+    if (endfree < size) {
+        /*
+         * There is enough space in the buffer, but not in
+         * one piece. We need to split the copy into two parts.
+         */
+        memcpy(rb->wp, buf, endfree);
+        memcpy(rb->buf, buf+endfree, size-endfree);
+        rb->wp = rb->buf + (size-endfree);
+    } else if (endfree > size) {
+        /*
+         * There is more space than needed at the end
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp += size;
+    } else {
+        /*
+         * There is exactly the space needed at the end.
+         * We need to wrap around the read pointer.
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp = rb->buf;
+    }
+
+    rb->free -= size;
+    rb->used += size;
+
+    ret = 0;
+
+out:
+    ASSERT_RB(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size byes from buffer into buf.
+ * Return -1 on error (not enough data in buffer)
+ */
+int alsaplug_ringbuffer_read(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int ret;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    ret = alsaplug_ringbuffer_read_locked(rb, buf, size);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size bytes from buffer into buf, assuming the buffer lock
+ * is already held.
+ * Return -1 on error (not enough data in buffer)
+ */
+int alsaplug_ringbuffer_read_locked(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int endused;
+
+    _ENTER;
+
+    ASSERT_RB(rb);
+
+    if (rb->used < size) {
+        /* Not enough bytes in buffer */
+        _LEAVE -1;
+    }
+
+    if (rb->rp < rb->wp) {
+        /*
+        Read pointer is behind write pointer, all the data is available in one chunk
+        */
+        memcpy(buf, rb->rp, size);
+        rb->rp += size;
+    } else {
+        /*
+         * Read pointer is before write pointer
+         */
+        endused = (rb->end - rb->rp)+1;
+
+        if (size < endused) {
+            /*
+             * Data is available in one chunk
+             */
+            memcpy(buf, rb->rp, size);
+            rb->rp += size;
+        } else {
+            /*
+             * There is enough data in the buffer, but it is fragmented.
+             */
+            memcpy(buf, rb->rp, endused);
+            memcpy(buf+endused, rb->buf, size-endused);
+            rb->rp = rb->buf + (size-endused);
+        }
+    }
+
+    rb->free += size;
+    rb->used -= size;
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Return the amount of free space currently in the rb
+ */
+unsigned int alsaplug_ringbuffer_free(alsaplug_ringbuf_t* rb) {
+
+    unsigned int f;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    f = alsaplug_ringbuffer_free_locked(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE f;
+}
+
+/*
+ * Return the amount of free space currently in the rb.
+ * Assume the rb lock is already being held.
+ */
+unsigned int alsaplug_ringbuffer_free_locked(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _LEAVE rb->free;
+}
+
+
+/*
+ * Return the amount of used space currently in the rb
+ */
+unsigned int alsaplug_ringbuffer_used(alsaplug_ringbuf_t* rb) {
+
+    unsigned int u;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    u = alsaplug_ringbuffer_used_locked(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE u;
+}
+
+/*
+ * Return the amount of used space currently in the rb.
+ * Assume the rb lock is already being held.
+ */
+unsigned int alsaplug_ringbuffer_used_locked(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _LEAVE rb->used;
+}
+
+
+/*
+ * destroy a ringbuffer
+ */
+void alsaplug_ringbuffer_destroy(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+    free(rb->buf);
+    if (rb->_free_lock) {
+        g_mutex_free(rb->lock);
+    }
+
+    _LEAVE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-ringbuffer.h	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,58 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ALSAPLUG_RINGBUFFER_H
+#define _ALSAPLUG_RINGBUFFER_H
+
+#include <glib.h>
+
+typedef GMutex alsaplug_ringbuffer_mutex_t;
+#define _ALSAPLUG_RINGBUFFER_LOCK(L) g_mutex_lock(L)
+#define _ALSAPLUG_RINGBUFFER_UNLOCK(L) g_mutex_unlock(L)
+
+#include <stdlib.h>
+
+#ifdef ALSAPLUG_RINGBUFFER_DEBUG
+#define ASSERT_RB(buf) _alsaplug_ringbuffer_assert(buf)
+#else
+#define ASSERT_RB(buf)
+#endif
+
+typedef struct {
+    alsaplug_ringbuffer_mutex_t* lock;
+    char _free_lock;
+    char* buf;
+    char* end;
+    char* wp;
+    char* rp;
+    unsigned int free;
+    unsigned int used;
+    unsigned int size;
+} alsaplug_ringbuf_t;
+
+int alsaplug_ringbuffer_init(alsaplug_ringbuf_t* rb, unsigned int size);
+int alsaplug_ringbuffer_init_with_lock(alsaplug_ringbuf_t* rb, unsigned int size, alsaplug_ringbuffer_mutex_t* lock);
+int alsaplug_ringbuffer_write(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+int alsaplug_ringbuffer_read(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+int alsaplug_ringbuffer_read_locked(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+void alsaplug_ringbuffer_reset(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_free(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_free_locked(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_used(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_used_locked(alsaplug_ringbuf_t* rb);
+void alsaplug_ringbuffer_destroy(alsaplug_ringbuf_t* rb);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-stdinc.h	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,36 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ALSA_STDINC_H_GUARD
+#define _ALSA_STDINC_H_GUARD
+
+#include <audacious/plugin.h>
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_plugin.h>
+
+#include <gtk/gtk.h>
+
+#include "alsa-debug.h"
+#include "alsa-types.h"
+#include "alsa-ringbuffer.h"
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-types.h	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,32 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ALSA_TYPES_H_GUARD
+#define _ALSA_TYPES_H_GUARD
+
+#include "alsa-stdinc.h"
+
+typedef struct {
+    AFormat aud_fmt;
+    snd_pcm_format_t alsa_fmt;
+} alsaplug_format_mapping_t;
+
+extern snd_pcm_format_t alsaplug_format_convert(AFormat aud_fmt);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/alsa-ng/alsa-util.c	Thu May 14 21:05:11 2009 -0500
@@ -0,0 +1,51 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "alsa-stdinc.h"
+
+static alsaplug_format_mapping_t alsaplug_format_conv_tbl[] = {
+    {FMT_S24_LE, SND_PCM_FORMAT_S24_LE},
+    {FMT_S24_BE, SND_PCM_FORMAT_S24_BE},
+    {FMT_S24_NE, SND_PCM_FORMAT_S24},
+    {FMT_U24_LE, SND_PCM_FORMAT_U24_LE},
+    {FMT_U24_BE, SND_PCM_FORMAT_U24_BE},
+    {FMT_U24_NE, SND_PCM_FORMAT_U24},
+    {FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
+    {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
+    {FMT_S16_NE, SND_PCM_FORMAT_S16},
+    {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
+    {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
+    {FMT_U16_NE, SND_PCM_FORMAT_U16},
+    {FMT_U8, SND_PCM_FORMAT_U8},
+    {FMT_S8, SND_PCM_FORMAT_S8},
+};
+
+snd_pcm_format_t
+alsaplug_format_convert(AFormat fmt)
+{
+    gint i;
+
+    for (i = 0; i < G_N_ELEMENTS(alsaplug_format_conv_tbl); i++)
+    {
+         if (alsaplug_format_conv_tbl[i].aud_fmt == fmt)
+             return alsaplug_format_conv_tbl[i].alsa_fmt;
+    }
+
+    return SND_PCM_FORMAT_UNKNOWN;
+}