changeset 930:2f742d127b3e trunk

[svn] - initial import of flacng from audacious-flacng-0.012
author nenolod
date Mon, 09 Apr 2007 10:55:23 -0700
parents 9631824411bf
children b6c95e2a14f4
files ChangeLog configure.ac mk/rules.mk.in src/flacng/AUTHORS src/flacng/Makefile src/flacng/README src/flacng/debug.h src/flacng/flac_compat112.h src/flacng/flac_compat113.h src/flacng/flac_compat114.h src/flacng/flacng.h src/flacng/plugin.c src/flacng/plugin.h src/flacng/seekable_stream_callbacks.c src/flacng/seekable_stream_callbacks.h src/flacng/tools.c src/flacng/tools.h src/flacng/version.h
diffstat 18 files changed, 1977 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Apr 09 08:51:31 2007 -0700
+++ b/ChangeLog	Mon Apr 09 10:55:23 2007 -0700
@@ -1,3 +1,11 @@
+2007-04-09 15:51:31 +0000  William Pitcock <nenolod@sacredspiral.co.uk>
+  revision [1986]
+  - use C-style vectors (part 2)
+  
+  trunk/src/madplug/SFMT-params19937.h |    8 ++++----
+  1 file changed, 4 insertions(+), 4 deletions(-)
+
+
 2007-04-09 15:50:46 +0000  William Pitcock <nenolod@sacredspiral.co.uk>
   revision [1984]
   - use C-style vectors instead of apple's stuff
--- a/configure.ac	Mon Apr 09 08:51:31 2007 -0700
+++ b/configure.ac	Mon Apr 09 10:55:23 2007 -0700
@@ -497,6 +497,29 @@
 	INPUT_PLUGINS="$INPUT_PLUGINS vorbis"
 fi
 
+dnl *** FLACng (in progress replacement to XMMS-flac port)
+
+AC_ARG_ENABLE(flacng,
+    [  --disable-flacng        disable flac input plugin (default=enabled)],
+    [enable_flacng=$enableval],
+    [enable_flacng="yes"]
+)
+
+if test "$enable_flacng" = "yes"; then
+    PKG_CHECK_MODULES(FLAC, [flac >= 1.1.2],
+        [have_flacng=yes],
+        [AC_MSG_WARN([*** Cannot find libFLAC, FLACng will not be built ***])
+         have_flacng=no]
+    )
+else
+    AC_MSG_RESULT([*** FLACng plugin disabled per user request ***])
+    have_flacng=no
+fi
+
+if test "$have_flacng" = "yes"; then
+	INPUT_PLUGINS="$INPUT_PLUGINS flacng"
+fi
+
 dnl *** WavPack 4.31 support
 
 AC_ARG_ENABLE(wavpack,
@@ -1266,6 +1289,7 @@
 echo "  Tone Generator:                         yes"
 echo "  Ogg Vorbis (vorbis):                    $have_oggvorbis"
 echo "  Free Lossless Audio Codec (flac):       $have_flac"
+echo "  Free Lossless Audio Codec (flacng):     $have_flacng"
 echo "  Commodore 64 audio (sid):               $have_sidplay"
 echo "  Game music (spc, nsf & gbs):            yes"
 echo "  PlayStation audio (sexypsf):            yes"
--- a/mk/rules.mk.in	Mon Apr 09 08:51:31 2007 -0700
+++ b/mk/rules.mk.in	Mon Apr 09 10:55:23 2007 -0700
@@ -202,8 +202,8 @@
 LIBBEEP_MAJOR_VERSION ?= @LIBBEEP_MAJOR_VERSION@
 LIBBEEP_MICRO_VERSION ?= @LIBBEEP_MICRO_VERSION@
 LIBBEEP_MINOR_VERSION ?= @LIBBEEP_MINOR_VERSION@
-LIBFLAC_CFLAGS ?= @LIBFLAC_CFLAGS@
-LIBFLAC_LIBS ?= @LIBFLAC_LIBS@
+FLAC_CFLAGS ?= @FLAC_CFLAGS@
+FLAC_LIBS ?= @FLAC_LIBS@
 LIBGLADE_CFLAGS ?= @LIBGLADE_CFLAGS@
 LIBGLADE_LIBS ?= @LIBGLADE_LIBS@
 LIBNMS_CFLAGS ?= @LIBNMS_CFLAGS@
@@ -358,3 +358,4 @@
 IMLIB2_CFLAGS ?= @IMLIB2_CFLAGS@
 IMLIB2_LIBS ?= @IMLIB2_LIBS@
 SIMD_CFLAGS ?= @SIMD_CFLAGS@
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/AUTHORS	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,28 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+Ralf Ertzinger <ralf@skytale.net>
+
+Thanks to Hendrik Scholz <http://www.wormulon.net> for testing the FreeBSD
+side
+
+William Pitcock <nenolod@sacredspiral.co.uk>
+    - tuple builder
+    - full transition to InputPlayback API
+    - full vorbiscomment support
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/Makefile	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,16 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = libflacng$(SHARED_SUFFIX)
+
+LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(FLAC_LIBS)
+
+SOURCES = plugin.c tools.c seekable_stream_callbacks.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) -I../../intl -I../.. -I./libflac $(FLAC_CFLAGS)
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/README	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,109 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+INTRODUCTION
+============
+
+This is an input plugin for the Audacious Media Player enabling the playback
+of FLAC compressed audio files.
+
+The Audacious Media Player (http://www.audacious-media-player.org) is a port of
+the XMMS audio player to GTK2.
+
+FLAC (http://flac.sourceforge.net/) is an Open Source lossless audio
+codec developed by Josh Coalson.
+
+
+
+STATUS
+======
+
+This plugin is to be considered of alpha quality. Use at your own risk.
+
+
+OK, let's be brief with this and first list the things that do _not_ work
+at the current time.
+
+- No display of bitrate
+- Visualizer plugins do not work with FLAC streams (*)
+- No OGGFlac streams (*)
+- Maximum of 2 channels (Stereo) (*)
+- No configuration options at all (*)
+
+Still interested? Good.
+The items marked with a (*) above can be fixed, this just takes some code
+that has not been written yet (har har).
+The FLAC decoder does not emit the current bitrate, so you'll have to
+settle for "VBR", which is true enough.
+As for configuration options, so far I have not found anything worth
+configuring, maybe you can think of something.
+
+Nonetheless.
+
+Given all the restrictions above, when presented with a 8/16/24 bit, stereo
+not-packed-in-ogg FLAC stream, this plugin will do it's best to produce
+some sound out of it. This may even work. It may, on the other hand, crash
+your machine and eat your data.
+
+Did I mention that the code is alpha quality?
+
+
+
+HOW TO BUILD
+============
+
+You will need the following to compile the code:
+
+1) A working compiler environment (if you do not know what this means
+   you probably do not want to use this code now, anyway)
+
+2) The Audacious media player, including development header files
+
+3) The FLAC libraries, including development header files
+
+If you have all this the rest is easy.
+Unpack the tar ball, change into the directory created and
+issue "configure" (without the quotes). This will try and find the
+necessary stuff on your system. When it finishes successfully, type
+"make". This will build the plugin library.
+To enable debugging messages (and you probably want those at this
+point) use "configure --debug"
+
+To install the plugin in your home directory (no special permissions
+needed, ideal for testing) use "make localinstall" (you can use
+"make localremove" to remove the plugin again). To install the plugin
+for all users use "make install" instead.
+
+
+
+TESTING
+=======
+
+After successful installation start Audacious (preferrably from the command
+line so you can see error or debug messages) and look in the configuration
+panel under plugins. The FLAC plugin ought to appear there, looking
+like "FLAC audio plugin". Now add a FLAC file to
+the playlist and try it.
+
+
+
+BUGS
+====
+
+Sure. Tons of 'em. Report to <ralf@skytale.net> for the time being.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/debug.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,35 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#define _ENTER _DEBUG("enter")
+#define _LEAVE _DEBUG("leave"); return
+#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: libflacng.so: %s:%d (%s): " string "\n", \
+    tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0)
+
+#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__)
+
+#ifdef DEBUG
+#define _DEBUG(...) _MESSAGE("DEBUG",  __VA_ARGS__)
+#else
+#define _DEBUG(...) {}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/flac_compat112.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,35 @@
+#ifndef FLAC_COMPAT112_H
+#define FLAC_COMPAT112_H
+
+#if !defined(FLAC_API_VERSION_CURRENT)
+
+#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
+
+#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus 
+#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
+#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
+#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
+#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
+
+#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
+#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
+#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
+#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
+#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
+#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
+
+FLAC__stream_decoder_init_stream(
+    FLAC__SeekableStreamDecoder*  decoder,
+    FLAC__SeekableStreamDecoderReadCallback read_callback,
+    FLAC__SeekableStreamDecoderSeekCallback     seek_callback,
+    FLAC__SeekableStreamDecoderTellCallback     tell_callback,
+    FLAC__SeekableStreamDecoderLengthCallback   length_callback,
+    FLAC__SeekableStreamDecoderEofCallback      eof_callback,
+    FLAC__SeekableStreamDecoderWriteCallback    write_callback,
+    FLAC__SeekableStreamDecoderMetadataCallback     metadata_callback,
+    FLAC__SeekableStreamDecoderErrorCallback    error_callback,
+    void *      client_data);
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/flac_compat113.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,7 @@
+#ifndef FLAC_COMPAT113_H
+#define FLAC_COMPAT113_H
+
+#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT == 7
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/flac_compat114.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,7 @@
+#ifndef FLAC_COMPAT114_H
+#define FLAC_COMPAT114_H
+
+#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT == 8
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/flacng.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,94 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef FLACNG_H
+#define FLACNG_H
+
+#include <glib.h>
+#include <audacious/plugin.h>
+#include <audacious/vfs.h>
+
+#define OUTPUT_BLOCK_SIZE (8192u)
+#define MAX_SUPPORTED_CHANNELS (2u)
+#define BUFFER_SIZE_SAMP (FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS)
+#define BUFFER_SIZE_BYTE (BUFFER_SIZE_SAMP * (FLAC__MAX_BITS_PER_SAMPLE/8))
+
+/*
+ * Audio information about the stream as a whole, gathered from
+ * the metadata header
+ */
+struct stream_info {
+    guint bits_per_sample;
+    guint samplerate;
+    guint channels;
+    gulong samples;
+    gboolean has_seektable;
+};
+
+/*
+ * Information about the last decoded audio frame.
+ * Also describes the format of the audio currently contained
+ * in the stream buffer.
+ */
+struct frame_info {
+    guint bits_per_sample;
+    guint samplerate;
+    guint channels;
+};
+
+/*
+ * Information about the stream content, from the metadata
+ */
+struct stream_comment {
+    gchar* artist;
+    gchar* album;
+    gchar* title;
+    gchar* tracknumber;
+    gchar* genre;
+    gchar* date;
+};
+
+/*
+ * Replaygain information, from the metadata
+ */
+struct stream_replaygain {
+    gboolean has_rg;
+    gchar* ref_loud;
+    gchar* track_gain;
+    gchar* track_peak;
+    gchar* album_gain;
+    gchar* album_peak;
+};
+
+
+typedef struct callback_info {
+    gint32* output_buffer;
+    gint32* write_pointer;
+    guint buffer_free;
+    guint buffer_used;
+    VFSFile* input_stream;
+    struct stream_info stream;
+    struct stream_comment comment;
+    struct stream_replaygain replaygain;
+    gboolean metadata_changed;
+    struct frame_info frame;
+    glong read_max;
+    gchar* name;
+} callback_info;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/plugin.c	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,706 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <audacious/util.h>
+#include <audacious/output.h>
+#include <glib/gi18n.h>
+#include "flacng.h"
+#include "tools.h"
+#include "plugin.h"
+#include "seekable_stream_callbacks.h"
+#include "version.h"
+#include "debug.h"
+
+static gchar *flac_fmts[] = { "flac", NULL };
+
+InputPlugin flac_ip = {
+    NULL,
+    NULL,
+    "FLACng Audio Plugin",
+    flac_init,
+    flac_aboutbox,
+    NULL,
+    flac_is_our_file,
+    NULL,
+    flac_play_file,
+    flac_stop,
+    flac_pause,
+    flac_seek,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    flac_get_song_info,
+    NULL,
+    NULL,
+    flac_get_song_tuple,	// get a tuple
+    NULL,
+    NULL,			// write a tuple back to a file as a tag
+/*    flac_is_our_fd */ NULL,	// version of is_our_file which is handed an FD
+    flac_fmts			// vector of fileextensions allowed by the plugin
+};
+
+FLAC__StreamDecoder* test_decoder;
+FLAC__StreamDecoder* main_decoder;
+callback_info* test_info;
+callback_info* main_info;
+gboolean plugin_initialized = FALSE;
+gint seek_to = -1;
+static GThread* thread;
+GMutex* flac_pl_mutex;
+
+/* === */
+
+InputPlugin* get_iplugin_info(void) {
+
+    _ENTER;
+
+    _DEBUG("%s (%s)", flac_ip.description, _VERSION);
+
+    _LEAVE &flac_ip;
+}
+
+/* --- */
+
+void flac_init(void) {
+
+    FLAC__StreamDecoderInitStatus ret;
+
+    _ENTER;
+
+    /*
+     * Callback structure and decoder for file test
+     * purposes
+     */
+    if (NULL == (test_info = init_callback_info("test"))) {
+        _ERROR("Could not initialize the test callback structure!");
+        _LEAVE;
+    }
+    _DEBUG("Test callback structure at %p", test_info);
+
+    if (NULL == (test_decoder = FLAC__stream_decoder_new())) {
+        _ERROR("Could not create the test FLAC decoder instance!");
+        _LEAVE;
+    }
+
+
+    /*
+     * We want the VORBISCOMMENT metadata, for the file tags
+     * and SEEKTABLE
+     */
+    FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+    FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_SEEKTABLE);
+
+    /*
+     * Callback structure and decoder for main decoding
+     * loop
+     */
+    if (NULL == (main_info = init_callback_info("main"))) {
+        _ERROR("Could not initialize the main callback structure!");
+        _LEAVE;
+    }
+    _DEBUG("Main callback structure at %p", main_info);
+
+    if (NULL == (main_decoder = FLAC__stream_decoder_new())) {
+        _ERROR("Could not create the main FLAC decoder instance!");
+        _LEAVE;
+    }
+
+
+    /*
+     * We want the VORBISCOMMENT metadata, for the file tags
+     * and SEEKTABLE
+     */
+    FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+    FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_SEEKTABLE);
+
+    /*
+     * Initialize decoders
+     */
+    if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream(
+            test_decoder,
+            read_callback,
+            seek_callback,
+            tell_callback,
+            length_callback,
+            eof_callback,
+            write_callback,
+            metadata_callback,
+            error_callback,
+            test_info))) {
+        _ERROR("Could not initialize test FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret);
+        _LEAVE;
+     }
+
+    if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream(
+            main_decoder,
+            read_callback,
+            seek_callback,
+            tell_callback,
+            length_callback,
+            eof_callback,
+            write_callback,
+            metadata_callback,
+            error_callback,
+            main_info))) {
+        _ERROR("Could not initialize main FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret);
+        _LEAVE;
+     }
+
+     /*
+      * Initialize the play loop mutex
+      */
+     flac_pl_mutex = g_mutex_new();
+
+     _DEBUG("plugin initialized OK!");
+     plugin_initialized = TRUE;
+    _LEAVE;
+}
+
+/* --- */
+
+gboolean flac_is_our_file(gchar* filename) {
+
+    _ENTER;
+
+    if (!plugin_initialized) {
+        _ERROR("Plugin not initialized!");
+        _LEAVE FALSE;
+    }
+
+    _DEBUG("Testing file: %s", filename);
+
+    if (FALSE == read_metadata(filename, test_decoder, test_info)) {
+        _DEBUG("File not handled by this plugin!");
+        _LEAVE FALSE;
+    }
+
+    /*
+     * See if the metadata has changed
+     */
+    if (FALSE == test_info->metadata_changed) {
+        _DEBUG("No metadata found in stream");
+        _LEAVE FALSE;
+    }
+
+    /*
+     * If we get here, the file is supported by FLAC.
+     * The stream characteristics have been filled in by
+     * the metadata callback.
+     * We can close the stream now.
+     */
+
+     vfs_fclose(test_info->input_stream);
+     test_info->input_stream = NULL;
+
+
+    _DEBUG("Stream encoded at %d Hz, %d bps, %d channels",
+        test_info->stream.samplerate,
+        test_info->stream.bits_per_sample,
+        test_info->stream.channels);
+
+    if (MAX_SUPPORTED_CHANNELS < test_info->stream.channels) {
+        _ERROR("This number of channels (%d) is currently not supported, stream not handled by this plugin",
+            test_info->stream.channels);
+        _LEAVE FALSE;
+    }
+
+    if ((16 != test_info->stream.bits_per_sample) &&
+        (24 != test_info->stream.bits_per_sample) &&
+        (8 != test_info->stream.bits_per_sample)) {
+        _ERROR("This number of bits (%d) is currently not supported, stream not handled by this plugin",
+            test_info->stream.bits_per_sample);
+        _LEAVE FALSE;
+    }
+
+    /*
+     * Looks good.
+     */
+
+    _DEBUG("Accepting file %s", filename);
+
+    reset_info(test_info);
+
+    _LEAVE TRUE;
+}
+
+/* --- */
+
+void squeeze_audio(gint32* src, void* dst, guint count, guint src_res, guint dst_res) {
+
+    /*
+     * Changes the sample width of count samples in src from
+     * src_res to dst_res and places the result in dst
+     */
+
+    gint i;
+    gint32* rp = src;
+    gint8* wp = dst;
+    gint16* wp2 = dst;
+    gint32* wp4 = dst;
+
+    _ENTER;
+
+    _DEBUG("Converting %d samples %d->%d", count, src_res, dst_res);
+
+    if ((src_res % 8 != 0) || (dst_res % 8 != 0)) {
+        _ERROR("Can not convert from %d bps to %d bps: not a multiple of 8",
+                src_res, dst_res);
+        _LEAVE;
+    }
+
+    if (16 == dst_res) {
+        if (8 == src_res) {
+            for (i=0; i<count; i++, wp2++, rp++) {
+                *wp2 = (*rp << 8) & 0xffff;
+            }
+        } else if (16 == src_res) {
+            for (i=0; i<count; i++, wp2++, rp++) {
+                *wp2 = *rp & 0xffff;
+            }
+        } else if (24 == src_res) {
+            for (i=0; i<count; i++, wp2++, rp++) {
+                *wp2 = (*rp >> 8) & 0xffff;
+            }
+        } else if (32 == src_res) {
+            for (i=0; i<count; i++, wp2++, rp++) {
+                *wp2 = (*rp >> 16) & 0xffff;
+            }
+        }
+    } else if (8 == dst_res) {
+        if (8 == src_res) {
+            for (i=0; i<count; i++, wp++, rp++) {
+                *wp = *rp & 0xff;
+            }
+        } else if (16 == src_res) {
+            for (i=0; i<count; i++, wp++, rp++) {
+                *wp = (*rp >> 8) & 0xff;
+            }
+        } else if (24 == src_res) {
+            for (i=0; i<count; i++, wp++, rp++) {
+                *wp = (*rp >> 16) & 0xff;
+            }
+        } else if (32 == src_res) {
+            for (i=0; i<count; i++, wp++, rp++) {
+                *wp = (*rp >> 24) & 0xff;
+            }
+        }
+    } else if (32 == dst_res) {
+        if (8 == src_res) {
+            for (i=0; i<count; i++, wp4++, rp++) {
+                *wp4 = (*rp << 24) & 0xffffffff;
+            }
+        } else if (16 == src_res) {
+            for (i=0; i<count; i++, wp4++, rp++) {
+                *wp4 = (*rp << 16) & 0xffffffff;
+            }
+        } else if (24 == src_res) {
+            for (i=0; i<count; i++, wp4++, rp++) {
+                *wp4 = (*rp << 8) & 0xffffffff;
+            }
+        } else if (32 == src_res) {
+            for (i=0; i<count; i++, wp4++, rp++) {
+                *wp4 = *rp;
+            }
+        }
+    }
+
+    _LEAVE;
+}
+
+/* --- */
+
+static gpointer flac_play_loop(gpointer arg) {
+
+    /*
+     * The main play loop.
+     * Decode a frame, push the decoded data to the output plugin
+     * chunkwise. Repeat until finished.
+     *
+     * Must be entered with flac_pl_mutex held!
+     */
+
+    gint ofree;
+    gint32* read_pointer;
+    gint elements_left;
+    gint seek_sample;
+    FLAC__StreamDecoderState state;
+    struct stream_info stream_info;
+    guint sample_count;
+    void* play_buffer;
+    InputPlayback* playback = (InputPlayback *) arg;
+
+    _ENTER;
+
+    if (NULL == (play_buffer = malloc(BUFFER_SIZE_BYTE))) {
+        _ERROR("Could not allocate conversion buffer");
+        playback->playing = FALSE;
+        g_mutex_unlock(flac_pl_mutex);
+        _LEAVE NULL;
+    }
+
+    stream_info.samplerate = main_info->stream.samplerate;
+    stream_info.channels = main_info->stream.channels;
+    main_info->metadata_changed = FALSE;
+
+    if (!playback->output->open_audio(FMT_S16_NE,
+        main_info->stream.samplerate,
+        main_info->stream.channels)) {
+        playback->playing = FALSE;
+        _ERROR("Could not open output plugin!");
+        g_mutex_unlock(flac_pl_mutex);
+        _LEAVE NULL;
+    }
+
+    while (TRUE == playback->playing) {
+
+        /*
+         * Try to decode a single frame of audio
+         */
+        if (FALSE == FLAC__stream_decoder_process_single(main_decoder)) {
+            /*
+             * Something went wrong
+             */
+            _ERROR("Error while decoding!");
+            break;
+        }
+
+        /*
+         * Maybe the metadata changed midstream. Check.
+         */
+
+        if (main_info->metadata_changed) {
+            /*
+             * Samplerate and channels are important
+             */
+            if (stream_info.samplerate != main_info->stream.samplerate) {
+                _ERROR("Samplerate changed midstream (now: %d, was: %d). This is not supported yet.",
+                    main_info->stream.samplerate, stream_info.samplerate);
+                break;
+            }
+            
+            if (stream_info.channels != main_info->stream.channels) {
+                _ERROR("Number of channels changed midstream (now: %d, was: %d). This is not supported yet.",
+                    main_info->stream.channels, stream_info.channels);
+                break;
+            }
+
+            main_info->metadata_changed = FALSE;
+        }
+
+        /*
+         * Compare the frame metadata to the current stream metadata
+         */
+        if (main_info->stream.samplerate != main_info->frame.samplerate) {
+            _ERROR("Frame samplerate mismatch (stream: %d, frame: %d)!",
+                main_info->stream.samplerate, main_info->frame.samplerate);
+            break;
+        }
+
+        if (main_info->stream.channels != main_info->frame.channels) {
+            _ERROR("Frame channel mismatch (stream: %d, frame: %d)!",
+                main_info->stream.channels, main_info->frame.channels);
+            break;
+        }
+
+        /*
+         * If the frame decoded was an audio frame we now
+         * have data in info->output_buffer
+         *
+         * The data in this buffer is in 32 bit wide samples, even if the
+         * real sample width is smaller. It has to be converted before
+         * feeding it to he output plugin.
+         *
+         * Feed the data in chunks of OUTPUT_BLOCK_SIZE elements to the output
+         * plugin
+         */
+        read_pointer = main_info->output_buffer;
+        elements_left = main_info->buffer_used;
+
+        while ((TRUE == playback->playing) && (0 != elements_left)) {
+
+            sample_count = MIN(OUTPUT_BLOCK_SIZE, elements_left);
+
+            /*
+             * Convert the audio.
+             * Currently this is hardcoded to 16 bit.
+             * 8 and 24 bit are sampled up/down linear.
+             */
+            _DEBUG("Converting %d samples to FMT_S16_NE", sample_count);
+            squeeze_audio(read_pointer, play_buffer, sample_count, main_info->frame.bits_per_sample, 16);
+
+            _DEBUG("Copying %d samples to output plugin", sample_count);
+
+            produce_audio(flac_ip.output->written_time(), FMT_S16_NE, main_info->frame.channels, sample_count * sizeof(gint16), play_buffer, NULL);
+
+            read_pointer += sample_count;
+            elements_left -= sample_count;
+
+            _DEBUG("%d elements left to be output", elements_left);
+        }
+
+        /*
+         * Clear the buffer.
+         */
+        main_info->write_pointer = main_info->output_buffer;
+        main_info->buffer_free = BUFFER_SIZE_SAMP;
+        main_info->buffer_used = 0;
+
+        /*
+         * Do we have to seek to somewhere?
+         */
+        if (-1 != seek_to) {
+            _DEBUG("Seek requested to %d seconds", seek_to);
+
+            if (FALSE == main_info->stream.has_seektable) {
+                _ERROR("Stream does not have a seektable, can not seek!");
+            } else {
+                seek_sample = seek_to * main_info->stream.samplerate;
+                _DEBUG("Seek requested to sample %d", seek_sample);
+                if (FALSE == FLAC__stream_decoder_seek_absolute(main_decoder, seek_sample)) {
+                    _ERROR("Could not seek to sample %d!", seek_sample);
+                } else {
+                    /*
+                     * Flush the buffers
+                     */
+                    flac_ip.output->flush(seek_to * 1000);
+                }
+            }
+            seek_to = -1;
+        }
+
+        /*
+         * Have we reached the end of the stream?
+         */
+        state = FLAC__stream_decoder_get_state(main_decoder);
+        if (0 == elements_left && (FLAC__STREAM_DECODER_END_OF_STREAM == state)) {
+            /*
+             * Yes. Drain the output buffer and stop playing.
+             */
+            
+            _DEBUG("End of stream reached, draining output buffer");
+
+            while((-1 == seek_to) && flac_ip.output->buffer_playing() && playback->playing == TRUE) {
+                g_usleep(40000);
+            }
+
+            if (-1 == seek_to) {
+                _DEBUG("Output buffer empty.");
+                playback->playing = FALSE;
+            }
+        }
+    }
+
+    /*
+     * Clean up a bit
+     */
+    playback->playing = FALSE;
+    _DEBUG("Closing audio device");
+    flac_ip.output->close_audio();
+    _DEBUG("Audio device closed");
+
+    free(play_buffer);
+
+    if (FALSE == FLAC__stream_decoder_flush(main_decoder)) {
+        _ERROR("Could not flush decoder state!");
+    }
+
+    /*
+     * Release the play loop mutex
+     */
+    g_mutex_unlock(flac_pl_mutex);
+    
+    g_thread_exit(NULL);
+
+    _LEAVE NULL;
+}
+
+/* --- */
+
+void flac_play_file (InputPlayback* input) {
+
+    gint l;
+
+    _ENTER;
+
+    if (!plugin_initialized) {
+        _ERROR("plugin not initialized!");
+        _LEAVE;
+    }
+
+    /*
+     * This will end a currently running decoder thread
+     */
+    input->playing = FALSE;
+    xmms_usleep(20000);
+
+    if (FALSE == read_metadata(input->filename, main_decoder, main_info)) {
+        _ERROR("Could not prepare file for playing!");
+        _LEAVE;
+    }
+
+    /*
+     * Calculate the length
+     */
+    if (0 == main_info->stream.samplerate) {
+        _ERROR("Invalid sample rate for stream!");
+        l = -1;
+    } else {
+        l = (main_info->stream.samples / main_info->stream.samplerate) * 1000;
+    }
+
+    /*
+     * Grab the play loop mutex
+     */
+    _DEBUG("Waiting for play loop mutex...");
+    g_mutex_lock(flac_pl_mutex);
+    _DEBUG("Got play loop mutex");
+
+    input->playing = TRUE;
+
+    flac_ip.set_info(get_title(input->filename, main_info), l, -1, main_info->stream.samplerate, main_info->stream.channels);
+
+    thread = g_thread_create(flac_play_loop, input, TRUE, NULL);
+
+    _LEAVE;
+}
+
+/* --- */
+
+void flac_stop(InputPlayback* input) {
+
+    _ENTER;
+
+    input->playing = FALSE;
+
+    _LEAVE;
+}
+
+/* --- */
+
+void flac_pause(InputPlayback* input, gshort p) {
+
+    _ENTER;
+
+    input->output->pause(p);
+
+    _LEAVE;
+}
+
+/* --- */
+
+void flac_seek(InputPlayback* input, gint time) {
+
+    _ENTER;
+
+    if (!input->playing) {
+        _DEBUG("Can not seek while not playing");
+        _LEAVE;
+    }
+
+    _DEBUG("Requesting seek to %d", time);
+    seek_to = time;
+
+    while (-1 != seek_to) {
+        xmms_usleep(10000);
+    }
+
+    _LEAVE;
+}
+
+/* --- */
+
+void flac_get_song_info(gchar* filename, gchar** title, gint* length) {
+
+    gint l;
+
+    _ENTER;
+
+    if (FALSE == read_metadata(filename, test_decoder, test_info)) {
+        _ERROR("Could not read file info!");
+        *length = -1;
+        *title = g_strdup("");
+        _LEAVE;
+    }
+
+    /*
+     * Calculate the stream length (milliseconds)
+     */
+    if (0 == test_info->stream.samplerate) {
+        _ERROR("Invalid sample rate for stream!");
+        l = -1;
+    } else {
+        l = (test_info->stream.samples / test_info->stream.samplerate) * 1000;
+        _DEBUG("Stream length: %d seconds", l/1000);
+    }
+
+    *length = l;
+    *title = get_title(filename, test_info);
+
+    reset_info(test_info);
+
+    _LEAVE;
+}
+
+/* --- */
+
+TitleInput *flac_get_song_tuple(gchar* filename) {
+
+    gint l;
+    TitleInput *tuple;
+
+    _ENTER;
+
+    if (FALSE == read_metadata(filename, test_decoder, test_info)) {
+        _ERROR("Could not read metadata tuple for file <%s>", filename);
+        _LEAVE NULL;
+    }
+
+    tuple = get_tuple(filename, test_info);
+
+    reset_info(test_info);
+
+    _LEAVE tuple;
+}
+
+/* --- */
+
+void flac_aboutbox(void) {
+
+    static GtkWidget* about_window;
+
+    if (about_window) {
+        gdk_window_raise(about_window->window);
+    }
+
+    about_window = xmms_show_message(_("About FLAC Audio Plugin"),
+                                     ("FLAC Audio Plugin (" _VERSION ")\n\n"
+                                      "Original code by\n"
+                                      "Ralf Ertzinger <ralf@skytale.net>\n"
+                                      "\n"
+                                      "http://www.skytale.net/projects/bmp-flac2/"),
+                                     _("OK"), FALSE, NULL, NULL);
+    g_signal_connect(G_OBJECT(about_window), "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &about_window);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/plugin.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,14 @@
+#ifndef _PLUGIN_H
+#define _PLUGIN_H
+
+void flac_init(void);
+void flac_aboutbox(void);
+gboolean flac_is_our_file(gchar* filename);
+void flac_play_file (InputPlayback* input);
+void flac_stop(InputPlayback* input);
+void flac_pause(InputPlayback* input, gshort p);
+void flac_seek(InputPlayback* input, gint time);
+void flac_get_song_info(gchar* filename, gchar** title, gint* length);
+TitleInput *flac_get_song_tuple(gchar* filename);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/seekable_stream_callbacks.c	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,338 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <FLAC/all.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <audacious/vfs.h>
+#include "flacng.h"
+#include "tools.h"
+#include "seekable_stream_callbacks.h"
+#include "debug.h"
+
+/* === */
+
+FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) {
+
+    callback_info* info;
+    gint to_read;
+    size_t read;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    if (NULL == info->input_stream) {
+        _ERROR("Trying to read data from an uninitialized file!");
+        _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+    }
+
+    if (0 <= info->read_max) {
+        to_read = MIN(*bytes, info->read_max);
+        _DEBUG("Reading restricted to %d bytes", info->read_max);
+    } else {
+        to_read = *bytes;
+    }
+
+    if (0 == to_read) {
+        _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+    }
+
+    read = vfs_fread(buffer, 1, to_read, info->input_stream);
+
+    if ((0 < read) && (0 < info->read_max)) {
+        info->read_max -= read;
+    }
+
+    _DEBUG("Wanted %d bytes, got %d bytes", *bytes, read);
+    *bytes = read;
+
+    switch(read) {
+        case -1:
+            _ERROR("Error while reading from stream!");
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+            break;
+
+        case 0:
+            _DEBUG("Stream reached EOF");
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+            break;
+
+        default:
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+    }
+}
+
+/* --- */
+
+FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
+
+    callback_info* info;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    _DEBUG("Seeking to %lld", absolute_byte_offset);
+
+    if (0 != vfs_fseek(info->input_stream, absolute_byte_offset, SEEK_SET)) {
+        _ERROR("Could not seek to %lld!", absolute_byte_offset);
+        _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+    }
+
+    _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {
+
+    callback_info* info;
+    glong position;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    if (-1 == (position = vfs_ftell(info->input_stream))) {
+        fprintf(stderr, "Could not tell current position!");
+        return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+    }
+
+    _DEBUG("Current position: %d", position);
+
+    *absolute_byte_offset = position;
+
+    _LEAVE FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data) {
+
+    callback_info* info;
+    gboolean eof;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    eof = vfs_feof(info->input_stream);
+
+    _LEAVE eof;
+}
+
+/* --- */
+
+FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
+
+    callback_info* info;
+    size_t size;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    *stream_length = 0;
+
+    _LEAVE FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) {
+
+    glong i;
+    gshort j;
+    gint32 sample;
+    callback_info* info;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    _DEBUG("Frame decoded: %d samples per channel, %d channels, %d bps",
+            frame->header.blocksize, frame->header.channels, frame->header.bits_per_sample);
+
+    /*
+     * Check if there is more data decoded than we have space
+     * for. This _should_ not happen given how our buffer is sized,
+     * but you never know.
+     */
+    if (info->buffer_free < (frame->header.blocksize * frame->header.channels)) {
+        _ERROR("BUG! Too much data decoded from stream!");
+        _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+    }
+
+    if ((frame->header.bits_per_sample != 8) &&
+        (frame->header.bits_per_sample != 16) &&
+        (frame->header.bits_per_sample != 24)) {
+        _ERROR("Unsupported bitrate found in stream: %d!", frame->header.bits_per_sample);
+        _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+    }
+
+    /*
+     * Copy the frame metadata, will be compared to stream
+     * metadata later
+     * This also describes the format of the current buffer content.
+     */
+    info->frame.channels = frame->header.channels;
+    info->frame.samplerate = frame->header.sample_rate;
+    info->frame.bits_per_sample = frame->header.bits_per_sample;
+
+    for (i=0; i < frame->header.blocksize; i++) {
+        for (j=0; j < frame->header.channels; j++) {
+            *(info->write_pointer++) = buffer[j][i];
+            info->buffer_free -= 1;
+            info->buffer_used += 1;
+        }
+    }
+
+    _DEBUG("free space in buffer after copying: %d samples", info->buffer_free);
+
+    _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+/* --- */
+
+void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
+
+    _ENTER;
+
+    _ERROR("FLAC decoder error callback was called: %d", status);
+
+    _LEAVE;
+
+}
+
+/* --- */
+
+void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
+
+    callback_info* info;
+    gint i;
+    FLAC__StreamMetadata_VorbisComment_Entry* entry;
+    FLAC__StreamMetadata* metadata_copy;
+    gchar* key;
+    gchar* value;
+    int artist_offset;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    /*
+     * We have found a metadata block. Enable unrestricted reading
+     */
+    info->read_max = -1;
+
+    if (FLAC__METADATA_TYPE_STREAMINFO == metadata->type) {
+        /*
+         * Basic stream information. Sample rate, channels and stuff
+         */
+        _DEBUG("FLAC__METADATA_TYPE_STREAMINFO found");
+
+        info->stream.samples = metadata->data.stream_info.total_samples;
+        _DEBUG("total_samples=%lld", metadata->data.stream_info.total_samples);
+        info->stream.bits_per_sample = metadata->data.stream_info.bits_per_sample;
+        _DEBUG("bits_per_sample=%d", metadata->data.stream_info.bits_per_sample);
+        info->stream.channels = metadata->data.stream_info.channels;
+        _DEBUG("channels=%d", metadata->data.stream_info.channels);
+        info->stream.samplerate = metadata->data.stream_info.sample_rate;
+        _DEBUG("sample_rate=%d", metadata->data.stream_info.sample_rate);
+
+        info->metadata_changed = TRUE;
+    }
+
+    if (FLAC__METADATA_TYPE_VORBIS_COMMENT == metadata->type) {
+        /*
+         * We will possibly need to modify some of the entries
+         * in the metadata field, so we make a copy of it
+         * first.
+         * The original structure must not be modified.
+         */
+        metadata_copy = FLAC__metadata_object_clone(metadata);
+
+        /*
+         * A vorbis type comment field.
+         */
+        _DEBUG("FLAC__METADATA_TYPE_VORBIS_COMMENT found");
+        _DEBUG("Vorbis comment contains %d fields", metadata_copy->data.vorbis_comment.num_comments);
+        _DEBUG("Vendor string: %s", metadata_copy->data.vorbis_comment.vendor_string.entry);
+
+        /*
+         * Find an ARTIST field
+         */
+        if (0 <= (artist_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata_copy, 0, "ARTIST"))) {
+            _DEBUG("ARTIST field found @ %d: %s", artist_offset,
+                    metadata_copy->data.vorbis_comment.comments[artist_offset].entry);
+        }
+
+
+        /*
+         * Enumerate the comment entries
+         */
+        entry = metadata_copy->data.vorbis_comment.comments;
+        for (i=0; i < metadata_copy->data.vorbis_comment.num_comments; i++,entry++) {
+            _DEBUG("Comment[%d]: %s", i, entry->entry);
+
+            /*
+             * Try and parse the comment.
+             * If FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair() succeeds,
+             * it allocates memory for the key and value which we have to take
+             * care of.
+             */
+            if (false == FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(*entry, &key, &value)) {
+                _DEBUG("Could not parse comment");
+            } else {
+                _DEBUG("Key: <%s>, Value <%s>", key, value);
+                add_comment(info, key, value);
+                free(key);
+                free(value);
+            }
+        }
+
+        /*
+         * Free our metadata copy
+         */
+        FLAC__metadata_object_delete(metadata_copy);
+
+        info->metadata_changed = TRUE;
+    }
+
+    if (FLAC__METADATA_TYPE_SEEKTABLE == metadata->type) {
+        /*
+         * We have found a seektable, which means that we can seek
+         * without telling FLAC the length of the file (which we can not
+         * do, since Audacious lacks the functions for that)
+         */
+        _DEBUG("FLAC__METADATA_TYPE_SEEKTABLE found");
+        info->stream.has_seektable = TRUE;
+    }
+
+    _LEAVE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/seekable_stream_callbacks.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,35 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+#include "flac_compat112.h"
+#include "flac_compat113.h"
+#include "flac_compat114.h"
+
+FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
+FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data);
+FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/tools.c	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,478 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <strings.h>
+#include "tools.h"
+#include "debug.h"
+
+/* === */
+
+callback_info* init_callback_info(gchar* name) {
+
+    callback_info* info;
+
+    _ENTER;
+
+    if (NULL == name) {
+        _ERROR("Can not allocate callback structure without a name");
+        _LEAVE NULL;
+    }
+
+    if (NULL == (info = malloc(sizeof(callback_info)))) {
+        _ERROR("Could not allocate memory for callback structure!");
+        _LEAVE NULL;
+    }
+
+    if (NULL == (info->output_buffer = malloc(BUFFER_SIZE_BYTE))) {
+        _ERROR("Could not allocate memory for output buffer!");
+        _LEAVE NULL;
+    }
+
+    /*
+     * We need to set this manually to NULL because
+     * reset_info will try to close the file pointed to
+     * by input_stream if it is non-NULL.
+     * Same for info->comment.x
+     */
+    info->name = name;
+    info->input_stream = NULL;
+    info->comment.artist = NULL;
+    info->comment.album = NULL;
+    info->comment.title = NULL;
+    info->comment.tracknumber = NULL;
+    info->comment.genre = NULL;
+    info->comment.date = NULL;
+    info->replaygain.ref_loud = NULL;
+    info->replaygain.track_gain = NULL;
+    info->replaygain.track_peak = NULL;
+    info->replaygain.album_gain = NULL;
+    info->replaygain.album_peak = NULL;
+    reset_info(info);
+
+    _DEBUG("Playback buffer allocated for %d samples, %d bytes", BUFFER_SIZE_SAMP, BUFFER_SIZE_BYTE);
+
+    _LEAVE info;
+}
+
+/* --- */
+
+void reset_info(callback_info* info) {
+
+    _ENTER;
+
+    _DEBUG("Using callback_info %s", info->name);
+
+    if (NULL != info->input_stream) {
+        vfs_fclose(info->input_stream);
+        info->input_stream = NULL;
+    }
+
+    // memset(info->output_buffer, 0, BUFFER_SIZE * sizeof(int16_t));
+    info->stream.samplerate = 0;
+    info->stream.bits_per_sample = 0;
+    info->stream.channels = 0;
+    info->stream.samples = 0;
+    info->stream.has_seektable = FALSE;
+    info->buffer_free = BUFFER_SIZE_SAMP;
+    info->buffer_used = 0;
+    info->write_pointer = info->output_buffer;
+    info->read_max = -1;
+
+    /*
+     * Clear the stream and frame information
+     */
+    info->stream.bits_per_sample = 0;
+    info->stream.samplerate = 0;
+    info->stream.channels = 0;
+    info->stream.samples = 0;
+
+    if (NULL != info->comment.artist) {
+        free(info->comment.artist);
+        info->comment.artist = NULL;
+    }
+
+    if (NULL != info->comment.album) {
+        free(info->comment.album);
+        info->comment.album = NULL;
+    }
+
+    if (NULL != info->comment.title) {
+        free(info->comment.title);
+        info->comment.title = NULL;
+    }
+
+    if (NULL != info->comment.tracknumber) {
+        free(info->comment.tracknumber);
+        info->comment.tracknumber = NULL;
+    }
+
+    if (NULL != info->comment.genre) {
+        free(info->comment.genre);
+        info->comment.genre = NULL;
+    }
+
+    if (NULL != info->replaygain.ref_loud) {
+        free(info->replaygain.ref_loud);
+        info->replaygain.ref_loud = NULL;
+    }
+
+    if (NULL != info->replaygain.track_gain) {
+        free(info->replaygain.track_gain);
+        info->replaygain.track_gain = NULL;
+    }
+
+    if (NULL != info->replaygain.track_peak) {
+        free(info->replaygain.track_peak);
+        info->replaygain.track_peak = NULL;
+    }
+
+    if (NULL != info->replaygain.album_gain) {
+        free(info->replaygain.album_gain);
+        info->replaygain.album_gain = NULL;
+    }
+
+    if (NULL != info->replaygain.album_peak) {
+        free(info->replaygain.album_peak);
+        info->replaygain.album_peak = NULL;
+    }
+
+    info->replaygain.has_rg = FALSE;
+
+    if (NULL != info->comment.date) {
+        free(info->comment.date);
+        info->comment.date = NULL;
+    }
+
+    info->frame.bits_per_sample = 0;
+    info->frame.samplerate = 0;
+    info->frame.channels = 0;
+
+    info->metadata_changed = FALSE;
+
+    _LEAVE;
+}
+
+/* --- */
+
+gchar* StreamDecoderInitState(FLAC__StreamDecoderInitStatus state) {
+
+    _ENTER;
+
+    switch(state) {
+        case FLAC__STREAM_DECODER_INIT_STATUS_OK:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_OK";
+            break;
+
+        case FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER";
+            break;
+
+        case FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS";
+            break;
+
+        case FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR";
+            break;
+
+        case FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE";
+            break;
+
+        case FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED:
+            _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED";
+            break;
+    }
+
+    _LEAVE "Unknown state";
+}
+
+/* --- */
+
+gchar* StreamDecoderState(FLAC__StreamDecoderState state) {
+
+    _ENTER;
+
+    switch(state) {
+
+        case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+            _LEAVE "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA";
+            break;
+
+        case FLAC__STREAM_DECODER_READ_METADATA:
+            _LEAVE "FLAC__STREAM_DECODER_READ_METADATA";
+            break;
+
+        case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+            _LEAVE "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC";
+            break;
+
+        case FLAC__STREAM_DECODER_READ_FRAME:
+            _LEAVE "FLAC__STREAM_DECODER_READ_FRAME";
+            break;
+
+        case FLAC__STREAM_DECODER_END_OF_STREAM:
+            _LEAVE "FLAC__STREAM_DECODER_END_OF_STREAM";
+            break;
+
+        case FLAC__STREAM_DECODER_ABORTED:
+            _LEAVE "FLAC__STREAM_DECODER_ABORTED";
+            break;
+
+        case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
+            _LEAVE "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR";
+            break;
+
+        case FLAC__STREAM_DECODER_UNINITIALIZED:
+            _LEAVE "FLAC__STREAM_DECODER_UNINITIALIZED";
+            break;
+
+    }
+
+    _LEAVE "";
+}
+
+/* --- */
+
+gboolean read_metadata(gchar* filename, FLAC__StreamDecoder* decoder, callback_info* info) {
+
+    FLAC__StreamDecoderState ret;
+
+    _ENTER;
+
+    _DEBUG("Using callback_info %s", info->name);
+
+    _DEBUG("Opening file %s", filename);
+
+    /*
+     * Reset the decoder
+     */
+    if (false == FLAC__stream_decoder_reset(decoder)) {
+        _ERROR("Could not reset the decoder!");
+        _LEAVE FALSE;
+    }
+
+    reset_info(info);
+
+    /*
+     * Just scan the first 8k for the start of metadata
+     */
+
+    info->read_max = 8192;
+
+    /*
+     * Open the file
+     */
+    if (NULL == (info->input_stream = vfs_fopen(filename, "rb"))) {
+        _ERROR("Could not open file for reading!");
+        _LEAVE FALSE;
+    }
+
+    /*
+     * Try to decode the metadata
+     */
+    if (false == FLAC__stream_decoder_process_until_end_of_metadata(decoder)) {
+        ret = FLAC__stream_decoder_get_state(decoder);
+        _DEBUG("Could not read the metadata: %s(%d)!", StreamDecoderState(ret), ret);
+        reset_info(info);
+        _LEAVE FALSE;
+    }
+
+    /*
+     * Seems to be a FLAC stream. Allow further reading
+     */
+
+    info->read_max = -1;
+
+    _LEAVE TRUE;
+}
+
+/* --- */
+
+static const gchar* get_extension(const gchar* filename) {
+
+    _ENTER;
+
+    const gchar *ext = strrchr(filename, '.');
+    _LEAVE ext ? ext + 1 : NULL;
+}
+
+/* --- */
+
+TitleInput* get_tuple(const gchar* filename, callback_info* info) {
+
+    TitleInput *out;
+
+    _ENTER;
+
+    _DEBUG("Using callback_info %s", info->name);
+
+    out = bmp_title_input_new();
+
+    out->file_name = g_path_get_basename(filename);
+    out->file_ext = get_extension(filename);
+    out->file_path = g_path_get_dirname(filename);
+
+    out->performer = g_strdup(info->comment.artist);
+    out->track_name = g_strdup(info->comment.title);
+    out->album_name = g_strdup(info->comment.album);
+    out->genre = g_strdup(info->comment.genre);
+    if (info->comment.tracknumber != NULL)
+        out->track_number = atoi(info->comment.tracknumber);
+    if (info->comment.date != NULL)
+        out->year = atoi(info->comment.date);
+
+    /*
+     * Calculate the stream length (milliseconds)
+     */
+    if (0 == info->stream.samplerate) {
+        _ERROR("Invalid sample rate for stream!");
+        out->length = -1;
+    } else {
+        out->length = (info->stream.samples / info->stream.samplerate) * 1000;
+        _DEBUG("Stream length: %d seconds", out->length / 1000);
+    }
+
+    _DEBUG("Tuple created: [%p]", out);
+
+    _LEAVE out;
+}
+
+/* --- */
+
+gchar* get_title(const gchar* filename, callback_info* info) {
+
+    TitleInput *input;
+    gchar *title;
+
+    _ENTER;
+
+    _DEBUG("Using callback_info %s", info->name);
+
+    input = get_tuple(filename, info);
+
+    if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input)))
+        title = g_strdup(input->file_name);
+
+    bmp_title_input_free(input);
+
+    _DEBUG("Title created: <%s>", title);
+
+    _LEAVE title;
+}
+
+/* --- */
+
+void add_comment(callback_info* info, gchar* key, gchar* value) {
+
+    /*
+     * We are currently interested in comments of type
+     * - ARTIST
+     * - ALBUM
+     * - TITLE
+     * - TRACKNUMBER
+     *
+     * We have to create copies of the value string.
+     */
+
+    gchar** destination = NULL;
+    gboolean rg = FALSE;
+
+     _ENTER;
+
+     _DEBUG("Using callback_info %s", info->name);
+
+    if (0 == strcasecmp(key, "ARTIST")) {
+        _DEBUG("Found key ARTIST");
+        destination = &(info->comment.artist);
+    }
+
+    if (0 == strcasecmp(key, "ALBUM")) {
+        _DEBUG("Found key ALBUM");
+        destination = &(info->comment.album);
+    }
+
+    if (0 == strcasecmp(key, "TITLE")) {
+        _DEBUG("Found key TITLE");
+        destination = &(info->comment.title);
+    }
+
+    if (0 == strcasecmp(key, "TRACKNUMBER")) {
+        _DEBUG("Found key TRACKNUMBER");
+        destination = &(info->comment.tracknumber);
+    }
+
+    if (0 == strcasecmp(key, "REPLAYGAIN_REFERENCE_LOUDNESS")) {
+        _DEBUG("Found key REPLAYGAIN_REFERENCE_LOUDNESS");
+        destination = &(info->replaygain.ref_loud);
+        rg = TRUE;
+    }
+
+    if (0 == strcasecmp(key, "REPLAYGAIN_TRACK_GAIN")) {
+        _DEBUG("Found key REPLAYGAIN_TRACK_GAIN");
+        destination = &(info->replaygain.track_gain);
+        rg = TRUE;
+    }
+
+    if (0 == strcasecmp(key, "REPLAYGAIN_TRACK_PEAK")) {
+        _DEBUG("Found key REPLAYGAIN_TRACK_PEAK");
+        destination = &(info->replaygain.track_peak);
+        rg = TRUE;
+    }
+
+    if (0 == strcasecmp(key, "REPLAYGAIN_ALBUM_GAIN")) {
+        _DEBUG("Found key REPLAYGAIN_ALBUM_GAIN");
+        destination = &(info->replaygain.album_gain);
+        rg = TRUE;
+    }
+
+    if (0 == strcasecmp(key, "REPLAYGAIN_ALBUM_PEAK")) {
+        _DEBUG("Found key REPLAYGAIN_ALBUM_PEAK");
+        destination = &(info->replaygain.album_peak);
+        rg = TRUE;
+    }
+
+    if (0 == strcasecmp(key, "DATE")) {
+        _DEBUG("Found key DATE");
+        destination = &(info->comment.date);
+    }
+
+    if (0 == strcasecmp(key, "GENRE")) {
+        _DEBUG("Found key GENRE");
+        destination = &(info->comment.genre);
+    }
+
+    if (NULL != destination) {
+        if (NULL != *destination) {
+            g_free(*destination);
+        }
+
+        if (NULL == (*destination = g_strdup(value))) {
+            _ERROR("Could not allocate memory for comment!");
+            _LEAVE;
+        }
+    }
+
+    if (rg) {
+        info->replaygain.has_rg = TRUE;
+    }
+
+    _LEAVE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/tools.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,34 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _TOOLS_H
+#define _TOOLS_H
+
+#include <glib.h>
+#include <FLAC/all.h>
+#include "flacng.h"
+
+callback_info* init_callback_info(gchar* name);
+void reset_info(callback_info* info);
+gchar* get_title(const gchar* filename, callback_info* info);
+TitleInput *get_tuple(const gchar *filename, callback_info* info);
+void add_comment(callback_info* info, gchar* key, gchar* value);
+gchar* StreamDecoderInitState(FLAC__StreamDecoderInitStatus);
+gboolean read_metadata(gchar* filename, FLAC__StreamDecoder* decoder, callback_info* info);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/version.h	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,6 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#define _VERSION "0.012 (Magnesium)"
+
+#endif