changeset 104:4b31176c198a trunk

[svn] - 1.1.2 compatible plugin (I HATE YOU FLAC PEOPLE)
author nenolod
date Mon, 23 Oct 2006 19:55:09 -0700
parents 117bc56d906b
children 03aaf25229d6
files ChangeLog src/flac112/Makefile src/flac112/Makefile.lite src/flac112/charset.c src/flac112/charset.h src/flac112/configure.c src/flac112/configure.h src/flac112/fast_float_math_hack.h src/flac112/file.c src/flac112/fileinfo.c src/flac112/grabbag.h src/flac112/grabbag/cuesheet.h src/flac112/grabbag/file.h src/flac112/grabbag/replaygain.h src/flac112/grabbag/seektable.h src/flac112/http.c src/flac112/http.h src/flac112/plugin.c src/flac112/plugin.h src/flac112/plugin_common/Makefile src/flac112/plugin_common/Makefile.lite src/flac112/plugin_common/README src/flac112/plugin_common/all.h src/flac112/plugin_common/charset.c src/flac112/plugin_common/charset.h src/flac112/plugin_common/defs.h src/flac112/plugin_common/dither.c src/flac112/plugin_common/dither.h src/flac112/plugin_common/locale_hack.h src/flac112/plugin_common/plugin_common_static.dsp src/flac112/plugin_common/tags.c src/flac112/plugin_common/tags.h src/flac112/replaygain.c src/flac112/replaygain_analysis.c src/flac112/replaygain_analysis.h src/flac112/replaygain_synthesis.c src/flac112/replaygain_synthesis.h src/flac112/tag.c src/flac112/tag.h
diffstat 39 files changed, 6699 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Oct 23 19:51:39 2006 -0700
+++ b/ChangeLog	Mon Oct 23 19:55:09 2006 -0700
@@ -1,3 +1,30 @@
+2006-10-24 02:51:39 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [206]
+  flac -> flac113
+  
+  trunk/src/flac113/Makefile               |   78 -
+  trunk/src/flac113/charset.c              |  388 +++---
+  trunk/src/flac113/charset.h              |  112 -
+  trunk/src/flac113/configure.c            | 1370 +++++++++++------------
+  trunk/src/flac113/configure.h            |  154 +-
+  trunk/src/flac113/fast_float_math_hack.h |   78 -
+  trunk/src/flac113/file.c                 |  284 ++--
+  trunk/src/flac113/fileinfo.c             | 1000 ++++++++---------
+  trunk/src/flac113/grabbag.h              |   66 -
+  trunk/src/flac113/http.c                 | 1794 +++++++++++++++----------------
+  trunk/src/flac113/http.h                 |   52 
+  trunk/src/flac113/plugin.c               | 1436 ++++++++++++------------
+  trunk/src/flac113/plugin.h               |   48 
+  trunk/src/flac113/replaygain.c           | 1330 +++++++++++-----------
+  trunk/src/flac113/replaygain_analysis.c  |  838 +++++++-------
+  trunk/src/flac113/replaygain_analysis.h  |  116 +-
+  trunk/src/flac113/replaygain_synthesis.c |  930 ++++++++--------
+  trunk/src/flac113/replaygain_synthesis.h |  102 -
+  trunk/src/flac113/tag.c                  |  298 ++---
+  trunk/src/flac113/tag.h                  |   50 
+  20 files changed, 5262 insertions(+), 5262 deletions(-)
+
+
 2006-10-24 02:46:25 +0000  William Pitcock <nenolod@nenolod.net>
   revision [204]
   - revert to r198
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/Makefile	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,39 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+SUBDIRS = plugin_common
+
+noinst_HEADERS = \
+	charset.h \
+	configure.h \
+	http.h \
+	plugin.h \
+	tag.h \
+	fast_float_math_hack.h \
+	replaygain_analysis.h \
+	grabbag.h \
+	replaygain_synthesis.h
+
+OBJECTIVE_LIBS = libflac$(SHARED_SUFFIX)
+
+LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR)
+
+LIBADD = $(LIBFLAC_LIBS)  -L./plugin_common -lplugin_common
+
+SOURCES = \
+	charset.c \
+	configure.c \
+	fileinfo.c \
+	http.c \
+	plugin.c \
+	tag.c \
+	replaygain_synthesis.c \
+	replaygain.c \
+	replaygain_analysis.c \
+	file.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) -I../../intl -I../.. $(LIBFLAC_CFLAGS)
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/Makefile.lite	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,39 @@
+#  libxmms-flac - XMMS FLAC input plugin
+#  Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
+#
+#  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.
+
+#
+# GNU makefile
+#
+
+topdir = ../..
+
+LIB_NAME  = libxmms-flac
+INCLUDES  = $(shell xmms-config --cflags) -I./include -I$(topdir)/include -I..
+# refer to the static libs explicitly
+LIBS = $(topdir)/obj/$(BUILD)/lib/libFLAC.a $(topdir)/obj/$(BUILD)/lib/libplugin_common.a $(topdir)/obj/$(BUILD)/lib/libgrabbag.a $(topdir)/obj/$(BUILD)/lib/libreplaygain_analysis.a $(topdir)/obj/$(BUILD)/lib/libreplaygain_synthesis.a -L$(ICONV_LIB_DIR) -liconv -lstdc++ -lz
+
+SRCS_C = \
+	charset.c \
+	configure.c \
+	plugin.c \
+	fileinfo.c \
+	http.c \
+	tag.c
+
+include $(topdir)/build/lib.mk
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/charset.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,194 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * Almost from charset.c
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 1999-2001  Håvard Kvålen <havardk@xmms.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 <stdlib.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "plugin_common/charset.h"
+#include "plugin_common/locale_hack.h"
+#include "charset.h"
+#include "configure.h"
+
+
+/****************
+ * Declarations *
+ ****************/
+
+#define CHARSET_TRANS_ARRAY_LEN ( sizeof(charset_trans_array) / sizeof((charset_trans_array)[0]) )
+const CharsetInfo charset_trans_array[] = {
+	{N_("Arabic (IBM-864)"),                  "IBM864"        },
+	{N_("Arabic (ISO-8859-6)"),               "ISO-8859-6"    },
+	{N_("Arabic (Windows-1256)"),             "windows-1256"  },
+	{N_("Baltic (ISO-8859-13)"),              "ISO-8859-13"   },
+	{N_("Baltic (ISO-8859-4)"),               "ISO-8859-4"    },
+	{N_("Baltic (Windows-1257)"),             "windows-1257"  },
+	{N_("Celtic (ISO-8859-14)"),              "ISO-8859-14"   },
+	{N_("Central European (IBM-852)"),        "IBM852"        },
+	{N_("Central European (ISO-8859-2)"),     "ISO-8859-2"    },
+	{N_("Central European (Windows-1250)"),   "windows-1250"  },
+	{N_("Chinese Simplified (GB18030)"),      "gb18030"       },
+	{N_("Chinese Simplified (GB2312)"),       "GB2312"        },
+	{N_("Chinese Traditional (Big5)"),        "Big5"          },
+	{N_("Chinese Traditional (Big5-HKSCS)"),  "Big5-HKSCS"    },
+	{N_("Cyrillic (IBM-855)"),                "IBM855"        },
+	{N_("Cyrillic (ISO-8859-5)"),             "ISO-8859-5"    },
+	{N_("Cyrillic (ISO-IR-111)"),             "ISO-IR-111"    },
+	{N_("Cyrillic (KOI8-R)"),                 "KOI8-R"        },
+	{N_("Cyrillic (Windows-1251)"),           "windows-1251"  },
+	{N_("Cyrillic/Russian (CP-866)"),         "IBM866"        },
+	{N_("Cyrillic/Ukrainian (KOI8-U)"),       "KOI8-U"        },
+	{N_("English (US-ASCII)"),                "us-ascii"      },
+	{N_("Greek (ISO-8859-7)"),                "ISO-8859-7"    },
+	{N_("Greek (Windows-1253)"),              "windows-1253"  },
+	{N_("Hebrew (IBM-862)"),                  "IBM862"        },
+	{N_("Hebrew (Windows-1255)"),             "windows-1255"  },
+	{N_("Japanese (EUC-JP)"),                 "EUC-JP"        },
+	{N_("Japanese (ISO-2022-JP)"),            "ISO-2022-JP"   },
+	{N_("Japanese (Shift_JIS)"),              "Shift_JIS"     },
+	{N_("Korean (EUC-KR)"),                   "EUC-KR"        },
+	{N_("Nordic (ISO-8859-10)"),              "ISO-8859-10"   },
+	{N_("South European (ISO-8859-3)"),       "ISO-8859-3"    },
+	{N_("Thai (TIS-620)"),                    "TIS-620"       },
+	{N_("Turkish (IBM-857)"),                 "IBM857"        },
+	{N_("Turkish (ISO-8859-9)"),              "ISO-8859-9"    },
+	{N_("Turkish (Windows-1254)"),            "windows-1254"  },
+	{N_("Unicode (UTF-7)"),                   "UTF-7"         },
+	{N_("Unicode (UTF-8)"),                   "UTF-8"         },
+	{N_("Unicode (UTF-16BE)"),                "UTF-16BE"      },
+	{N_("Unicode (UTF-16LE)"),                "UTF-16LE"      },
+	{N_("Unicode (UTF-32BE)"),                "UTF-32BE"      },
+	{N_("Unicode (UTF-32LE)"),                "UTF-32LE"      },
+	{N_("Vietnamese (VISCII)"),               "VISCII"        },
+	{N_("Vietnamese (Windows-1258)"),         "windows-1258"  },
+	{N_("Visual Hebrew (ISO-8859-8)"),        "ISO-8859-8"    },
+	{N_("Western (IBM-850)"),                 "IBM850"        },
+	{N_("Western (ISO-8859-1)"),              "ISO-8859-1"    },
+	{N_("Western (ISO-8859-15)"),             "ISO-8859-15"   },
+	{N_("Western (Windows-1252)"),            "windows-1252"  }
+
+	/*
+	 * From this point, character sets aren't supported by iconv
+	 */
+#if 0
+	{N_("Arabic (IBM-864-I)"),                "IBM864i"              },
+	{N_("Arabic (ISO-8859-6-E)"),             "ISO-8859-6-E"         },
+	{N_("Arabic (ISO-8859-6-I)"),             "ISO-8859-6-I"         },
+	{N_("Arabic (MacArabic)"),                "x-mac-arabic"         },
+	{N_("Armenian (ARMSCII-8)"),              "armscii-8"            },
+	{N_("Central European (MacCE)"),          "x-mac-ce"             },
+	{N_("Chinese Simplified (GBK)"),          "x-gbk"                },
+	{N_("Chinese Simplified (HZ)"),           "HZ-GB-2312"           },
+	{N_("Chinese Traditional (EUC-TW)"),      "x-euc-tw"             },
+	{N_("Croatian (MacCroatian)"),            "x-mac-croatian"       },
+	{N_("Cyrillic (MacCyrillic)"),            "x-mac-cyrillic"       },
+	{N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian"      },
+	{N_("Farsi (MacFarsi)"),                  "x-mac-farsi"},
+	{N_("Greek (MacGreek)"),                  "x-mac-greek"          },
+	{N_("Gujarati (MacGujarati)"),            "x-mac-gujarati"       },
+	{N_("Gurmukhi (MacGurmukhi)"),            "x-mac-gurmukhi"       },
+	{N_("Hebrew (ISO-8859-8-E)"),             "ISO-8859-8-E"         },
+	{N_("Hebrew (ISO-8859-8-I)"),             "ISO-8859-8-I"         },
+	{N_("Hebrew (MacHebrew)"),                "x-mac-hebrew"         },
+	{N_("Hindi (MacDevanagari)"),             "x-mac-devanagari"     },
+	{N_("Icelandic (MacIcelandic)"),          "x-mac-icelandic"      },
+	{N_("Korean (JOHAB)"),                    "x-johab"              },
+	{N_("Korean (UHC)"),                      "x-windows-949"        },
+	{N_("Romanian (MacRomanian)"),            "x-mac-romanian"       },
+	{N_("Turkish (MacTurkish)"),              "x-mac-turkish"        },
+	{N_("User Defined"),                      "x-user-defined"       },
+	{N_("Vietnamese (TCVN)"),                 "x-viet-tcvn5712"      },
+	{N_("Vietnamese (VPS)"),                  "x-viet-vps"           },
+	{N_("Western (MacRoman)"),                "x-mac-roman"          },
+	/* charsets whithout posibly translatable names */
+	{"T61.8bit",                              "T61.8bit"             },
+	{"x-imap4-modified-utf7",                 "x-imap4-modified-utf7"},
+	{"x-u-escaped",                           "x-u-escaped"          },
+	{"windows-936",                           "windows-936"          }
+#endif
+};
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Commons conversion functions
+ */
+char *convert_from_utf8_to_user(const char *string)
+{
+	return FLAC_plugin__charset_convert_string(string, "UTF-8", flac_cfg.title.user_char_set);
+}
+
+char *convert_from_user_to_utf8(const char *string)
+{
+	return FLAC_plugin__charset_convert_string(string, flac_cfg.title.user_char_set, "UTF-8");
+}
+
+GList *Charset_Create_List (void)
+{
+	GList *list = NULL;
+	guint i;
+
+	for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+		list = g_list_append(list,_(charset_trans_array[i].charset_title));
+	return list;
+}
+
+GList *Charset_Create_List_UTF8_Only (void)
+{
+	GList *list = NULL;
+
+	list = g_list_append(list,_(Charset_Get_Title_From_Name("UTF-8")));
+	return list;
+}
+
+
+/*
+ * Return charset_name from charset_title
+ */
+gchar *Charset_Get_Name_From_Title (const gchar *charset_title)
+{
+	guint i;
+
+	if (charset_title)
+		for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+			if ( strcasecmp(_(charset_title),_(charset_trans_array[i].charset_title)) == 0 )
+				return charset_trans_array[i].charset_name;
+	return "";
+}
+
+
+/*
+ * Return charset_title from charset_name
+ */
+gchar *Charset_Get_Title_From_Name (const gchar *charset_name)
+{
+	guint i;
+
+	if (charset_name)
+		for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+			if ( strcasecmp(charset_name,charset_trans_array[i].charset_name) == 0 )
+				return _(charset_trans_array[i].charset_title);
+	return "";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/charset.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,56 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * Almost from charset.h - 2001/12/04
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 1999-2001  H蛆ard Kv虱en <havardk@xmms.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 __CHARSET_H__
+#define __CHARSET_H__
+
+
+/***************
+ * Declaration *
+ ***************/
+
+typedef struct {
+	gchar *charset_title;
+	gchar *charset_name;
+} CharsetInfo;
+
+/* translated charset titles */
+extern const CharsetInfo charset_trans_array[];
+
+/**************
+ * Prototypes *
+ **************/
+
+/*
+ * The returned strings are malloc()ed an must be free()d by the caller
+ */
+char *convert_from_utf8_to_user(const char *string);
+char *convert_from_user_to_utf8(const char *string);
+
+GList *Charset_Create_List (void);
+GList *Charset_Create_List_UTF8_Only (void);
+gchar *Charset_Get_Name_From_Title (const gchar *charset_title);
+gchar *Charset_Get_Title_From_Name (const gchar *charset_name);
+
+#endif /* __CHARSET_H__ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/configure.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,685 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * Based on mpg123 plugin
+ *          and prefs.c - 2000/05/06
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 2000-2002  Jerome Couderc <j.couderc@ifrance.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <math.h>
+
+#include <audacious/configdb.h>
+#include <audacious/dirbrowser.h>
+#include <audacious/titlestring.h>
+#include <audacious/util.h>
+#include <audacious/plugin.h>
+
+#include "plugin_common/locale_hack.h"
+#include "replaygain_synthesis.h" /* for NOISE_SHAPING_LOW */
+#include "charset.h"
+#include "configure.h"
+
+/*
+ * Initialize Global Valueable
+ */
+flac_config_t flac_cfg = {
+	/* title */
+	{
+		FALSE, /* tag_override */
+		NULL, /* tag_format */
+		FALSE, /* convert_char_set */
+		NULL /* user_char_set */
+	},
+	/* stream */
+	{
+		100 /* KB */, /* http_buffer_size */
+		50, /* http_prebuffer */
+		FALSE, /* use_proxy */
+		"", /* proxy_host */
+		0, /* proxy_port */
+		FALSE, /* proxy_use_auth */
+		"", /* proxy_user */
+		"", /* proxy_pass */
+		FALSE, /* save_http_stream */
+		"", /* save_http_path */
+		FALSE, /* cast_title_streaming */
+		FALSE /* use_udp_channel */
+	},
+	/* output */
+	{
+		/* replaygain */
+		{
+			FALSE, /* enable */
+			TRUE, /* album_mode */
+			0, /* preamp */
+			FALSE /* hard_limit */
+		},
+		/* resolution */
+		{
+			/* normal */
+			{
+				TRUE /* dither_24_to_16 */
+			},
+			/* replaygain */
+			{
+				TRUE, /* dither */
+				NOISE_SHAPING_LOW, /* noise_shaping */
+				16 /* bps_out */
+			}
+		}
+	}
+};
+
+
+static GtkWidget *flac_configurewin = NULL;
+static GtkWidget *vbox, *notebook;
+
+static GtkWidget *title_tag_override, *title_tag_box, *title_tag_entry, *title_desc;
+static GtkWidget *convert_char_set, *fileCharacterSetEntry, *userCharacterSetEntry;
+static GtkWidget *replaygain_enable, *replaygain_album_mode;
+static GtkWidget *replaygain_preamp_hscale, *replaygain_preamp_label, *replaygain_hard_limit;
+static GtkObject *replaygain_preamp;
+static GtkWidget *resolution_normal_dither_24_to_16;
+static GtkWidget *resolution_replaygain_dither;
+static GtkWidget *resolution_replaygain_noise_shaping_frame;
+static GtkWidget *resolution_replaygain_noise_shaping_radio_none;
+static GtkWidget *resolution_replaygain_noise_shaping_radio_low;
+static GtkWidget *resolution_replaygain_noise_shaping_radio_medium;
+static GtkWidget *resolution_replaygain_noise_shaping_radio_high;
+static GtkWidget *resolution_replaygain_bps_out_frame;
+static GtkWidget *resolution_replaygain_bps_out_radio_16bps;
+static GtkWidget *resolution_replaygain_bps_out_radio_24bps;
+
+static GtkObject *streaming_size_adj, *streaming_pre_adj;
+static GtkWidget *streaming_save_use, *streaming_save_entry;
+#ifdef FLAC_ICECAST
+static GtkWidget *streaming_cast_title, *streaming_udp_title;
+#endif
+static GtkWidget *streaming_save_dirbrowser;
+static GtkWidget *streaming_save_hbox;
+
+static const gchar *gtk_entry_get_text_1 (GtkWidget *widget);
+static void flac_configurewin_ok(GtkWidget * widget, gpointer data);
+static void configure_destroy(GtkWidget * w, gpointer data);
+
+static void flac_configurewin_ok(GtkWidget * widget, gpointer data)
+{
+	ConfigDb *db;
+
+	(void)widget, (void)data; /* unused arguments */
+	g_free(flac_cfg.title.tag_format);
+	flac_cfg.title.tag_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_tag_entry)));
+	flac_cfg.title.user_char_set = Charset_Get_Name_From_Title(gtk_entry_get_text_1(userCharacterSetEntry));
+
+	db = bmp_cfg_db_open();
+	/* title */
+	bmp_cfg_db_set_bool(db, "flac", "title.tag_override", flac_cfg.title.tag_override);
+	bmp_cfg_db_set_string(db, "flac", "title.tag_format", flac_cfg.title.tag_format);
+	bmp_cfg_db_set_bool(db, "flac", "title.convert_char_set", flac_cfg.title.convert_char_set);
+	bmp_cfg_db_set_string(db, "flac", "title.user_char_set", flac_cfg.title.user_char_set);
+	/* output */
+	bmp_cfg_db_set_bool(db, "flac", "output.replaygain.enable", flac_cfg.output.replaygain.enable);
+	bmp_cfg_db_set_bool(db, "flac", "output.replaygain.album_mode", flac_cfg.output.replaygain.album_mode);
+	bmp_cfg_db_set_int(db, "flac", "output.replaygain.preamp", flac_cfg.output.replaygain.preamp);
+	bmp_cfg_db_set_bool(db, "flac", "output.replaygain.hard_limit", flac_cfg.output.replaygain.hard_limit);
+	bmp_cfg_db_set_bool(db, "flac", "output.resolution.normal.dither_24_to_16", flac_cfg.output.resolution.normal.dither_24_to_16);
+	bmp_cfg_db_set_bool(db, "flac", "output.resolution.replaygain.dither", flac_cfg.output.resolution.replaygain.dither);
+	bmp_cfg_db_set_int(db, "flac", "output.resolution.replaygain.noise_shaping", flac_cfg.output.resolution.replaygain.noise_shaping);
+	bmp_cfg_db_set_int(db, "flac", "output.resolution.replaygain.bps_out", flac_cfg.output.resolution.replaygain.bps_out);
+	/* streaming */
+	flac_cfg.stream.http_buffer_size = (gint) GTK_ADJUSTMENT(streaming_size_adj)->value;
+	flac_cfg.stream.http_prebuffer = (gint) GTK_ADJUSTMENT(streaming_pre_adj)->value;
+
+	flac_cfg.stream.save_http_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use));
+
+	if (flac_cfg.stream.save_http_path != NULL)
+		g_free(flac_cfg.stream.save_http_path);
+
+	flac_cfg.stream.save_http_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_save_entry)));
+
+#ifdef FLAC_ICECAST
+	flac_cfg.stream.cast_title_streaming = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_cast_title));
+	flac_cfg.stream.use_udp_channel = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_udp_title));
+#endif
+
+	bmp_cfg_db_set_int(db, "flac", "stream.http_buffer_size", flac_cfg.stream.http_buffer_size);
+	bmp_cfg_db_set_int(db, "flac", "stream.http_prebuffer", flac_cfg.stream.http_prebuffer);
+	bmp_cfg_db_set_bool(db, "flac", "stream.save_http_stream", flac_cfg.stream.save_http_stream);
+	bmp_cfg_db_set_string(db, "flac", "stream.save_http_path", flac_cfg.stream.save_http_path);
+#ifdef FLAC_ICECAST
+	bmp_cfg_db_set_bool(db, "flac", "stream.cast_title_streaming", flac_cfg.stream.cast_title_streaming);
+	bmp_cfg_db_set_bool(db, "flac", "stream.use_udp_channel", flac_cfg.stream.use_udp_channel);
+#endif
+
+	bmp_cfg_db_close(db);
+	gtk_widget_destroy(flac_configurewin);
+}
+
+static void configure_destroy(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+}
+
+static void title_tag_override_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.title.tag_override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_tag_override));
+
+	gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override);
+	gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override);
+
+}
+
+static void convert_char_set_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.title.convert_char_set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(convert_char_set));
+
+	gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE);
+	gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set);
+}
+
+static void replaygain_enable_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.replaygain.enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_enable));
+
+	gtk_widget_set_sensitive(replaygain_album_mode, flac_cfg.output.replaygain.enable);
+	gtk_widget_set_sensitive(replaygain_preamp_hscale, flac_cfg.output.replaygain.enable);
+	gtk_widget_set_sensitive(replaygain_hard_limit, flac_cfg.output.replaygain.enable);
+}
+
+static void replaygain_album_mode_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.replaygain.album_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_album_mode));
+}
+
+static void replaygain_hard_limit_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.replaygain.hard_limit = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit));
+}
+
+static void replaygain_preamp_cb(GtkWidget *widget, gpointer data)
+{
+	GString *gstring = g_string_new("");
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.replaygain.preamp = (int) floor(GTK_ADJUSTMENT(replaygain_preamp)->value + 0.5);
+
+	g_string_sprintf(gstring, "%i dB", flac_cfg.output.replaygain.preamp);
+	gtk_label_set_text(GTK_LABEL(replaygain_preamp_label), _(gstring->str));
+}
+
+static void resolution_normal_dither_24_to_16_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.resolution.normal.dither_24_to_16 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16));
+}
+
+static void resolution_replaygain_dither_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.resolution.replaygain.dither = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither));
+
+	gtk_widget_set_sensitive(resolution_replaygain_noise_shaping_frame, flac_cfg.output.resolution.replaygain.dither);
+}
+
+static void resolution_replaygain_noise_shaping_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.resolution.replaygain.noise_shaping =
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none))? 0 :
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low))? 1 :
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium))? 2 :
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high))? 3 :
+		0
+	;
+}
+
+static void resolution_replaygain_bps_out_cb(GtkWidget *widget, gpointer data)
+{
+	(void)widget, (void)data; /* unused arguments */
+	flac_cfg.output.resolution.replaygain.bps_out =
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps))? 16 :
+		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps))? 24 :
+		16
+	;
+}
+
+static void streaming_save_dirbrowser_cb(gchar * dir)
+{
+	gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), dir);
+}
+
+static void streaming_save_browse_cb(GtkWidget * w, gpointer data)
+{
+	(void) w;
+	(void) data;
+	if (!streaming_save_dirbrowser)
+	{
+		streaming_save_dirbrowser = xmms_create_dir_browser(_("Select the directory where you want to store the MPEG streams:"),
+								    flac_cfg.stream.save_http_path, GTK_SELECTION_SINGLE, streaming_save_dirbrowser_cb);
+		gtk_signal_connect(GTK_OBJECT(streaming_save_dirbrowser), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &streaming_save_dirbrowser);
+		gtk_window_set_transient_for(GTK_WINDOW(streaming_save_dirbrowser), GTK_WINDOW(flac_configurewin));
+		gtk_widget_show(streaming_save_dirbrowser);
+	}
+}
+
+static void streaming_save_use_cb(GtkWidget * w, gpointer data)
+{
+	gboolean save_stream;
+	(void) w;
+	(void) data;
+
+	save_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use));
+
+	gtk_widget_set_sensitive(streaming_save_hbox, save_stream);
+}
+
+
+void FLAC_XMMS__configure(void)
+{
+	GtkWidget *title_frame, *title_tag_vbox, *title_tag_label;
+	GtkWidget *replaygain_frame, *resolution_frame, *output_vbox, *resolution_normal_frame, *resolution_replaygain_frame;
+	GtkWidget *replaygain_vbox, *resolution_hbox, *resolution_normal_vbox, *resolution_replaygain_vbox;
+	GtkWidget *resolution_replaygain_noise_shaping_vbox;
+	GtkWidget *resolution_replaygain_bps_out_vbox;
+	GtkWidget *label, *hbox;
+	GtkWidget *bbox, *ok, *cancel;
+	GList *list;
+
+	GtkWidget *streaming_vbox;
+	GtkWidget *streaming_buf_frame, *streaming_buf_hbox;
+	GtkWidget *streaming_size_box, *streaming_size_label, *streaming_size_spin;
+	GtkWidget *streaming_pre_box, *streaming_pre_label, *streaming_pre_spin;
+	GtkWidget *streaming_save_frame, *streaming_save_vbox;
+	GtkWidget *streaming_save_label, *streaming_save_browse;
+#ifdef FLAC_ICECAST
+	GtkWidget *streaming_cast_frame, *streaming_cast_vbox;
+#endif
+
+	if (flac_configurewin != NULL) {
+		gdk_window_raise(flac_configurewin->window);
+		return;
+	}
+	flac_configurewin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &flac_configurewin);
+	gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(configure_destroy), &flac_configurewin);
+	gtk_window_set_title(GTK_WINDOW(flac_configurewin), _("Flac Configuration"));
+	gtk_window_set_policy(GTK_WINDOW(flac_configurewin), FALSE, FALSE, FALSE);
+	gtk_container_border_width(GTK_CONTAINER(flac_configurewin), 10);
+
+	vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_add(GTK_CONTAINER(flac_configurewin), vbox);
+
+	notebook = gtk_notebook_new();
+	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+	/* Title config.. */
+
+	title_frame = gtk_frame_new(_("Tag Handling"));
+	gtk_container_border_width(GTK_CONTAINER(title_frame), 5);
+
+	title_tag_vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(title_tag_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(title_frame), title_tag_vbox);
+
+	/* Convert Char Set */
+
+	convert_char_set = gtk_check_button_new_with_label(_("Convert Character Set"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(convert_char_set), flac_cfg.title.convert_char_set);
+	gtk_signal_connect(GTK_OBJECT(convert_char_set), "clicked", (GCallback)convert_char_set_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), convert_char_set, FALSE, FALSE, 0);
+	/*  Combo boxes... */
+	hbox = gtk_hbox_new(FALSE,4);
+	gtk_container_add(GTK_CONTAINER(title_tag_vbox),hbox);
+	label = gtk_label_new(_("Convert character set from :"));
+	gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0);
+	fileCharacterSetEntry = gtk_combo_new();
+	gtk_box_pack_start(GTK_BOX(hbox),fileCharacterSetEntry,TRUE,TRUE,0);
+
+	label = gtk_label_new (_("to :"));
+	gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0);
+	userCharacterSetEntry = gtk_combo_new();
+	gtk_box_pack_start(GTK_BOX(hbox),userCharacterSetEntry,TRUE,TRUE,0);
+
+	gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(fileCharacterSetEntry)->entry),FALSE);
+	gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),FALSE);
+	gtk_combo_set_value_in_list(GTK_COMBO(fileCharacterSetEntry),TRUE,FALSE);
+	gtk_combo_set_value_in_list(GTK_COMBO(userCharacterSetEntry),TRUE,FALSE);
+
+	list = Charset_Create_List();
+	gtk_combo_set_popdown_strings(GTK_COMBO(fileCharacterSetEntry),Charset_Create_List_UTF8_Only());
+	gtk_combo_set_popdown_strings(GTK_COMBO(userCharacterSetEntry),list);
+	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),Charset_Get_Title_From_Name(flac_cfg.title.user_char_set));
+	gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE);
+	gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set);
+
+	/* Override Tagging Format */
+
+	title_tag_override = gtk_check_button_new_with_label(_("Override generic titles"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_tag_override), flac_cfg.title.tag_override);
+	gtk_signal_connect(GTK_OBJECT(title_tag_override), "clicked", (GCallback)title_tag_override_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_override, FALSE, FALSE, 0);
+
+	title_tag_box = gtk_hbox_new(FALSE, 5);
+	gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_box, FALSE, FALSE, 0);
+
+	title_tag_label = gtk_label_new(_("Title format:"));
+	gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_label, FALSE, FALSE, 0);
+
+	title_tag_entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(title_tag_entry), flac_cfg.title.tag_format);
+	gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_entry, TRUE, TRUE, 0);
+
+	title_desc = xmms_titlestring_descriptions("pafFetnygc", 2);
+	gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_desc, FALSE, FALSE, 0);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame, gtk_label_new(_("Title")));
+
+	/* Output config.. */
+
+	output_vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(output_vbox), 5);
+
+	/* replaygain */
+
+	replaygain_frame = gtk_frame_new(_("ReplayGain"));
+	gtk_container_border_width(GTK_CONTAINER(replaygain_frame), 5);
+	gtk_box_pack_start(GTK_BOX(output_vbox), replaygain_frame, TRUE, TRUE, 0);
+
+	replaygain_vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(replaygain_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(replaygain_frame), replaygain_vbox);
+
+	replaygain_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_enable), flac_cfg.output.replaygain.enable);
+	gtk_signal_connect(GTK_OBJECT(replaygain_enable), "clicked", (GCallback)replaygain_enable_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_enable, FALSE, FALSE, 0);
+
+	replaygain_album_mode = gtk_check_button_new_with_label(_("Album mode"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_album_mode), flac_cfg.output.replaygain.album_mode);
+	gtk_signal_connect(GTK_OBJECT(replaygain_album_mode), "clicked", (GCallback)replaygain_album_mode_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_album_mode, FALSE, FALSE, 0);
+
+	hbox = gtk_hbox_new(FALSE,3);
+	gtk_container_add(GTK_CONTAINER(replaygain_vbox),hbox);
+	label = gtk_label_new(_("Preamp:"));
+	gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0);
+	replaygain_preamp = gtk_adjustment_new(flac_cfg.output.replaygain.preamp, -24.0, +24.0, 1.0, 6.0, 0.0);
+	gtk_signal_connect(GTK_OBJECT(replaygain_preamp), "value-changed", (GCallback)replaygain_preamp_cb, NULL);
+	replaygain_preamp_hscale = gtk_hscale_new(GTK_ADJUSTMENT(replaygain_preamp));
+	gtk_scale_set_draw_value(GTK_SCALE(replaygain_preamp_hscale), FALSE);
+	gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_hscale,TRUE,TRUE,0);
+	replaygain_preamp_label = gtk_label_new(_("0 dB"));
+	gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_label,FALSE,FALSE,0);
+	gtk_adjustment_value_changed(GTK_ADJUSTMENT(replaygain_preamp));
+
+	replaygain_hard_limit = gtk_check_button_new_with_label(_("6dB hard limiting"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit), flac_cfg.output.replaygain.hard_limit);
+	gtk_signal_connect(GTK_OBJECT(replaygain_hard_limit), "clicked", (GCallback)replaygain_hard_limit_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_hard_limit, FALSE, FALSE, 0);
+
+	replaygain_enable_cb(replaygain_enable, NULL);
+
+	/* resolution */
+
+	resolution_frame = gtk_frame_new(_("Resolution"));
+	gtk_container_border_width(GTK_CONTAINER(resolution_frame), 5);
+	gtk_box_pack_start(GTK_BOX(output_vbox), resolution_frame, TRUE, TRUE, 0);
+
+	resolution_hbox = gtk_hbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(resolution_hbox), 5);
+	gtk_container_add(GTK_CONTAINER(resolution_frame), resolution_hbox);
+
+	resolution_normal_frame = gtk_frame_new(_("Without ReplayGain"));
+	gtk_container_border_width(GTK_CONTAINER(resolution_normal_frame), 5);
+	gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_normal_frame, TRUE, TRUE, 0);
+
+	resolution_normal_vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(resolution_normal_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(resolution_normal_frame), resolution_normal_vbox);
+
+	resolution_normal_dither_24_to_16 = gtk_check_button_new_with_label(_("Dither 24bps to 16bps"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16), flac_cfg.output.resolution.normal.dither_24_to_16);
+	gtk_signal_connect(GTK_OBJECT(resolution_normal_dither_24_to_16), "clicked", (GCallback)resolution_normal_dither_24_to_16_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(resolution_normal_vbox), resolution_normal_dither_24_to_16, FALSE, FALSE, 0);
+
+	resolution_replaygain_frame = gtk_frame_new(_("With ReplayGain"));
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_frame), 5);
+	gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_replaygain_frame, TRUE, TRUE, 0);
+
+	resolution_replaygain_vbox = gtk_vbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_frame), resolution_replaygain_vbox);
+
+	resolution_replaygain_dither = gtk_check_button_new_with_label(_("Enable dithering"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither), flac_cfg.output.resolution.replaygain.dither);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_dither), "clicked", (GCallback)resolution_replaygain_dither_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), resolution_replaygain_dither, FALSE, FALSE, 0);
+
+	hbox = gtk_hbox_new(FALSE, 10);
+	gtk_container_border_width(GTK_CONTAINER(hbox), 5);
+	gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), hbox, TRUE, TRUE, 0);
+
+	resolution_replaygain_noise_shaping_frame = gtk_frame_new(_("Noise shaping"));
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), 5);
+	gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_noise_shaping_frame, TRUE, TRUE, 0);
+
+	resolution_replaygain_noise_shaping_vbox = gtk_vbutton_box_new();
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), resolution_replaygain_noise_shaping_vbox);
+
+	resolution_replaygain_noise_shaping_radio_none = gtk_radio_button_new_with_label(NULL, _("none"));
+	if(flac_cfg.output.resolution.replaygain.noise_shaping == 0)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_none), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_none);
+
+	resolution_replaygain_noise_shaping_radio_low = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("low"));
+	if(flac_cfg.output.resolution.replaygain.noise_shaping == 1)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_low), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_low);
+
+	resolution_replaygain_noise_shaping_radio_medium = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("medium"));
+	if(flac_cfg.output.resolution.replaygain.noise_shaping == 2)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_medium), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_medium);
+
+	resolution_replaygain_noise_shaping_radio_high = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("high"));
+	if(flac_cfg.output.resolution.replaygain.noise_shaping == 3)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_high), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_high);
+
+	resolution_replaygain_bps_out_frame = gtk_frame_new(_("Dither to"));
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_frame), 5);
+	gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_bps_out_frame, FALSE, FALSE, 0);
+
+	resolution_replaygain_bps_out_vbox = gtk_vbutton_box_new();
+	gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), 0);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_frame), resolution_replaygain_bps_out_vbox);
+
+	resolution_replaygain_bps_out_radio_16bps = gtk_radio_button_new_with_label(NULL, _("16 bps"));
+	if(flac_cfg.output.resolution.replaygain.bps_out == 16)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_16bps), "clicked", (GCallback)resolution_replaygain_bps_out_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_16bps);
+
+	resolution_replaygain_bps_out_radio_24bps = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_bps_out_radio_16bps), _("24 bps"));
+	if(flac_cfg.output.resolution.replaygain.bps_out == 24)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps), TRUE);
+	gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_24bps), "clicked", (GCallback)resolution_replaygain_bps_out_cb, NULL);
+	gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_24bps);
+
+	resolution_replaygain_dither_cb(resolution_replaygain_dither, NULL);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), output_vbox, gtk_label_new(_("Output")));
+
+	/* Streaming */
+
+	streaming_vbox = gtk_vbox_new(FALSE, 0);
+
+	streaming_buf_frame = gtk_frame_new(_("Buffering:"));
+	gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_frame), 5);
+	gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_buf_frame, FALSE, FALSE, 0);
+
+	streaming_buf_hbox = gtk_hbox_new(TRUE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_hbox), 5);
+	gtk_container_add(GTK_CONTAINER(streaming_buf_frame), streaming_buf_hbox);
+
+	streaming_size_box = gtk_hbox_new(FALSE, 5);
+	/*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_size_box,0,1,0,1); */
+	gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_size_box, TRUE, TRUE, 0);
+	streaming_size_label = gtk_label_new(_("Buffer size (kb):"));
+	gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_label, FALSE, FALSE, 0);
+	streaming_size_adj = gtk_adjustment_new(flac_cfg.stream.http_buffer_size, 4, 4096, 4, 4, 4);
+	streaming_size_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_size_adj), 8, 0);
+	gtk_widget_set_usize(streaming_size_spin, 60, -1);
+	gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_spin, FALSE, FALSE, 0);
+
+	streaming_pre_box = gtk_hbox_new(FALSE, 5);
+	/*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_pre_box,1,2,0,1); */
+	gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_pre_box, TRUE, TRUE, 0);
+	streaming_pre_label = gtk_label_new(_("Pre-buffer (percent):"));
+	gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_label, FALSE, FALSE, 0);
+	streaming_pre_adj = gtk_adjustment_new(flac_cfg.stream.http_prebuffer, 0, 90, 1, 1, 1);
+	streaming_pre_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_pre_adj), 1, 0);
+	gtk_widget_set_usize(streaming_pre_spin, 60, -1);
+	gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_spin, FALSE, FALSE, 0);
+
+	/*
+	 * Save to disk config.
+	 */
+	streaming_save_frame = gtk_frame_new(_("Save stream to disk:"));
+	gtk_container_set_border_width(GTK_CONTAINER(streaming_save_frame), 5);
+	gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_save_frame, FALSE, FALSE, 0);
+
+	streaming_save_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(streaming_save_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(streaming_save_frame), streaming_save_vbox);
+
+	streaming_save_use = gtk_check_button_new_with_label(_("Save stream to disk"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_save_use), flac_cfg.stream.save_http_stream);
+	gtk_signal_connect(GTK_OBJECT(streaming_save_use), "clicked", GTK_SIGNAL_FUNC(streaming_save_use_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_use, FALSE, FALSE, 0);
+
+	streaming_save_hbox = gtk_hbox_new(FALSE, 5);
+	gtk_widget_set_sensitive(streaming_save_hbox, flac_cfg.stream.save_http_stream);
+	gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_hbox, FALSE, FALSE, 0);
+
+	streaming_save_label = gtk_label_new(_("Path:"));
+	gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_label, FALSE, FALSE, 0);
+
+	streaming_save_entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), flac_cfg.stream.save_http_path);
+	gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_entry, TRUE, TRUE, 0);
+
+	streaming_save_browse = gtk_button_new_with_label(_("Browse"));
+	gtk_signal_connect(GTK_OBJECT(streaming_save_browse), "clicked", GTK_SIGNAL_FUNC(streaming_save_browse_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_browse, FALSE, FALSE, 0);
+
+#ifdef FLAC_ICECAST
+	streaming_cast_frame = gtk_frame_new(_("SHOUT/Icecast:"));
+	gtk_container_set_border_width(GTK_CONTAINER(streaming_cast_frame), 5);
+	gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_cast_frame, FALSE, FALSE, 0);
+
+	streaming_cast_vbox = gtk_vbox_new(5, FALSE);
+	gtk_container_add(GTK_CONTAINER(streaming_cast_frame), streaming_cast_vbox);
+
+	streaming_cast_title = gtk_check_button_new_with_label(_("Enable SHOUT/Icecast title streaming"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_cast_title), flac_cfg.stream.cast_title_streaming);
+	gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_cast_title, FALSE, FALSE, 0);
+
+	streaming_udp_title = gtk_check_button_new_with_label(_("Enable Icecast Metadata UDP Channel"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_udp_title), flac_cfg.stream.use_udp_channel);
+	gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_udp_title, FALSE, FALSE, 0);
+#endif
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), streaming_vbox, gtk_label_new(_("Streaming")));
+
+	/* Buttons */
+
+	bbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+	gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+	ok = gtk_button_new_with_label(_("Ok"));
+	gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(flac_configurewin_ok), NULL);
+	GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
+	gtk_widget_grab_default(ok);
+
+	cancel = gtk_button_new_with_label(_("Cancel"));
+	gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(flac_configurewin));
+	GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+	gtk_widget_show_all(flac_configurewin);
+}
+
+void FLAC_XMMS__aboutbox()
+{
+	static GtkWidget *about_window;
+
+	if (about_window)
+		gdk_window_raise(about_window->window);
+	else
+	{
+		about_window = xmms_show_message(
+			_("About Flac Plugin"),
+			_("Flac Plugin by Josh Coalson\n"
+			  "contributions by\n"
+			  "......\n"
+			  "......\n"
+			  "and\n"
+			  "Daisuke Shimamura\n"
+			  "Visit http://flac.sourceforge.net/"),
+			_("Ok"), FALSE, NULL, NULL);
+		gtk_signal_connect(GTK_OBJECT(about_window), "destroy",
+				   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+				   &about_window);
+	}
+}
+
+/*
+ * Get text of an Entry or a ComboBox
+ */
+static const gchar *gtk_entry_get_text_1 (GtkWidget *widget)
+{
+	if (GTK_IS_COMBO(widget))
+	{
+		return gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(widget)->entry));
+	}else if (GTK_IS_ENTRY(widget))
+	{
+		return gtk_entry_get_text(GTK_ENTRY(widget));
+	}else
+	{
+		return NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/configure.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,77 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * Based on mpg123 plugin
+ *
+ * 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 __CONFIGURE_H__
+#define __CONFIGURE_H__
+
+#include <glib.h>
+
+typedef struct {
+	struct {
+		gboolean tag_override;
+		gchar *tag_format;
+		gboolean convert_char_set;
+		gchar *user_char_set;
+	} title;
+
+	struct {
+		gint http_buffer_size;
+		gint http_prebuffer;
+		gboolean use_proxy; 
+		gchar *proxy_host;
+		gint proxy_port;
+		gboolean proxy_use_auth;
+		gchar *proxy_user;
+		gchar	*proxy_pass;
+		gboolean save_http_stream;
+		gchar *save_http_path;
+		gboolean cast_title_streaming;
+		gboolean use_udp_channel;
+	} stream;
+
+	struct {
+		struct {
+			gboolean enable;
+			gboolean album_mode;
+			gint preamp;
+			gboolean hard_limit;
+		} replaygain;
+		struct {
+			struct {
+				gboolean dither_24_to_16;
+			} normal;
+			struct {
+				gboolean dither;
+				gint noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */
+				gint bps_out;
+			} replaygain;
+		} resolution;
+	} output;
+} flac_config_t;
+
+extern flac_config_t flac_cfg;
+
+extern void FLAC_XMMS__configure(void);
+extern void FLAC_XMMS__aboutbox();
+
+#endif
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/fast_float_math_hack.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,39 @@
+#   ifdef __ICL /* only Intel C compiler has fmath ??? */
+
+    #include <mathf.h>
+
+/* Nearest integer, absolute value, etc. */
+
+    #define ceil ceilf
+    #define fabs fabsf
+    #define floor floorf
+    #define fmod fmodf
+    #define rint rintf
+    #define hypot hypotf
+
+/* Power functions */
+
+    #define pow powf
+    #define sqrt sqrtf
+
+/* Exponential and logarithmic functions */
+
+    #define exp expf
+    #define log logf
+    #define log10 log10f
+
+/* Trigonometric functions */
+
+    #define acos acosf
+    #define asin asinf
+    #define atan atanf
+    #define cos cosf
+    #define sin sinf
+    #define tan tanf
+
+/* Hyperbolic functions */
+    #define cosh coshf
+    #define sinh sinhf
+    #define tanh tanhf
+
+#   endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/file.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,142 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+
+#if defined _MSC_VER || defined __MINGW32__
+#include <sys/utime.h> /* for utime() */
+#include <io.h> /* for chmod(), _setmode(), unlink() */
+#include <fcntl.h> /* for _O_BINARY */
+#else
+#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
+#include <utime.h> /* for utime() */
+#endif
+#ifdef __CYGWIN__
+#include <io.h> /* for setmode(), O_BINARY */
+#include <fcntl.h> /* for _O_BINARY */
+#endif
+#include <sys/stat.h> /* for stat(), maybe chmod() */
+#if defined _WIN32 && !defined __CYGWIN__
+#else
+#include <unistd.h> /* for unlink() */
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* for strrchr() */
+#include "grabbag.h"
+
+
+void grabbag__file_copy_metadata(const char *srcpath, const char *destpath)
+{
+	struct stat srcstat;
+	struct utimbuf srctime;
+
+	if(0 == stat(srcpath, &srcstat)) {
+		srctime.actime = srcstat.st_atime;
+		srctime.modtime = srcstat.st_mtime;
+		(void)chmod(destpath, srcstat.st_mode);
+		(void)utime(destpath, &srctime);
+	}
+}
+
+off_t grabbag__file_get_filesize(const char *srcpath)
+{
+	struct stat srcstat;
+
+	if(0 == stat(srcpath, &srcstat))
+		return srcstat.st_size;
+	else
+		return -1;
+}
+
+const char *grabbag__file_get_basename(const char *srcpath)
+{
+	const char *p;
+
+	p = strrchr(srcpath, '/');
+	if(0 == p) {
+		p = strrchr(srcpath, '\\');
+		if(0 == p)
+			return srcpath;
+	}
+	return ++p;
+}
+
+FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only)
+{
+	struct stat stats;
+
+	if(0 == stat(filename, &stats)) {
+#if !defined _MSC_VER && !defined __MINGW32__
+		if(read_only) {
+			stats.st_mode &= ~S_IWUSR;
+			stats.st_mode &= ~S_IWGRP;
+			stats.st_mode &= ~S_IWOTH;
+		}
+		else {
+			stats.st_mode |= S_IWUSR;
+		}
+#else
+		if(read_only)
+			stats.st_mode &= ~S_IWRITE;
+		else
+			stats.st_mode |= S_IWRITE;
+#endif
+		if(0 != chmod(filename, stats.st_mode))
+			return false;
+	}
+	else
+		return false;
+
+	return true;
+}
+
+FLAC__bool grabbag__file_remove_file(const char *filename)
+{
+	return grabbag__file_change_stats(filename, /*read_only=*/false) && 0 == unlink(filename);
+}
+
+FILE *grabbag__file_get_binary_stdin()
+{
+	/* if something breaks here it is probably due to the presence or
+	 * absence of an underscore before the identifiers 'setmode',
+	 * 'fileno', and/or 'O_BINARY'; check your system header files.
+	 */
+#if defined _MSC_VER || defined __MINGW32__
+	_setmode(_fileno(stdin), _O_BINARY);
+#elif defined __CYGWIN__
+	/* almost certainly not needed for any modern Cygwin, but let's be safe... */
+	setmode(_fileno(stdin), _O_BINARY);
+#endif
+
+	return stdin;
+}
+
+FILE *grabbag__file_get_binary_stdout()
+{
+	/* if something breaks here it is probably due to the presence or
+	 * absence of an underscore before the identifiers 'setmode',
+	 * 'fileno', and/or 'O_BINARY'; check your system header files.
+	 */
+#if defined _MSC_VER || defined __MINGW32__
+	_setmode(_fileno(stdout), _O_BINARY);
+#elif defined __CYGWIN__
+	/* almost certainly not needed for any modern Cygwin, but let's be safe... */
+	setmode(_fileno(stdout), _O_BINARY);
+#endif
+
+	return stdout;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/fileinfo.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,433 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *  Copyright (C) 1999,2000  Håvard Kvålen
+ *  Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ *  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 <stdlib.h>
+#include <string.h> /* for strlen() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <gtk/gtk.h>
+
+#include "audacious/util.h"
+#include "FLAC/metadata.h"
+#include "charset.h"
+#include "configure.h"
+#include "plugin_common/tags.h"
+#include "plugin_common/locale_hack.h"
+
+static GtkWidget *window = NULL;
+static GList *genre_list = NULL;
+static GtkWidget *filename_entry, *tag_frame;
+static GtkWidget *title_entry, *artist_entry, *album_entry, *date_entry, *tracknum_entry, *comment_entry;
+static GtkWidget *genre_combo;
+static GtkWidget *flac_samplerate, *flac_channels, *flac_bits_per_sample, *flac_blocksize, *flac_filesize, *flac_samples, *flac_bitrate;
+
+static gchar *current_filename = NULL;
+static FLAC__StreamMetadata *tags_ = NULL;
+
+static const gchar *vorbis_genres[] =
+{
+	N_("Blues"), N_("Classic Rock"), N_("Country"), N_("Dance"),
+	N_("Disco"), N_("Funk"), N_("Grunge"), N_("Hip-Hop"),
+	N_("Jazz"), N_("Metal"), N_("New Age"), N_("Oldies"),
+	N_("Other"), N_("Pop"), N_("R&B"), N_("Rap"), N_("Reggae"),
+	N_("Rock"), N_("Techno"), N_("Industrial"), N_("Alternative"),
+	N_("Ska"), N_("Death Metal"), N_("Pranks"), N_("Soundtrack"),
+	N_("Euro-Techno"), N_("Ambient"), N_("Trip-Hop"), N_("Vocal"),
+	N_("Jazz+Funk"), N_("Fusion"), N_("Trance"), N_("Classical"),
+	N_("Instrumental"), N_("Acid"), N_("House"), N_("Game"),
+	N_("Sound Clip"), N_("Gospel"), N_("Noise"), N_("Alt"),
+	N_("Bass"), N_("Soul"), N_("Punk"), N_("Space"),
+	N_("Meditative"), N_("Instrumental Pop"),
+	N_("Instrumental Rock"), N_("Ethnic"), N_("Gothic"),
+	N_("Darkwave"), N_("Techno-Industrial"), N_("Electronic"),
+	N_("Pop-Folk"), N_("Eurodance"), N_("Dream"),
+	N_("Southern Rock"), N_("Comedy"), N_("Cult"),
+	N_("Gangsta Rap"), N_("Top 40"), N_("Christian Rap"),
+	N_("Pop/Funk"), N_("Jungle"), N_("Native American"),
+	N_("Cabaret"), N_("New Wave"), N_("Psychedelic"), N_("Rave"),
+	N_("Showtunes"), N_("Trailer"), N_("Lo-Fi"), N_("Tribal"),
+	N_("Acid Punk"), N_("Acid Jazz"), N_("Polka"), N_("Retro"),
+	N_("Musical"), N_("Rock & Roll"), N_("Hard Rock"), N_("Folk"),
+	N_("Folk/Rock"), N_("National Folk"), N_("Swing"),
+	N_("Fast-Fusion"), N_("Bebob"), N_("Latin"), N_("Revival"),
+	N_("Celtic"), N_("Bluegrass"), N_("Avantgarde"),
+	N_("Gothic Rock"), N_("Progressive Rock"),
+	N_("Psychedelic Rock"), N_("Symphonic Rock"), N_("Slow Rock"),
+	N_("Big Band"), N_("Chorus"), N_("Easy Listening"),
+	N_("Acoustic"), N_("Humour"), N_("Speech"), N_("Chanson"),
+	N_("Opera"), N_("Chamber Music"), N_("Sonata"), N_("Symphony"),
+	N_("Booty Bass"), N_("Primus"), N_("Porn Groove"),
+	N_("Satire"), N_("Slow Jam"), N_("Club"), N_("Tango"),
+	N_("Samba"), N_("Folklore"), N_("Ballad"), N_("Power Ballad"),
+	N_("Rhythmic Soul"), N_("Freestyle"), N_("Duet"),
+	N_("Punk Rock"), N_("Drum Solo"), N_("A Cappella"),
+	N_("Euro-House"), N_("Dance Hall"), N_("Goa"),
+	N_("Drum & Bass"), N_("Club-House"), N_("Hardcore"),
+	N_("Terror"), N_("Indie"), N_("BritPop"), N_("Negerpunk"),
+	N_("Polsk Punk"), N_("Beat"), N_("Christian Gangsta Rap"),
+	N_("Heavy Metal"), N_("Black Metal"), N_("Crossover"),
+	N_("Contemporary Christian"), N_("Christian Rock"),
+	N_("Merengue"), N_("Salsa"), N_("Thrash Metal"),
+	N_("Anime"), N_("JPop"), N_("Synthpop")
+};
+
+static void label_set_text(GtkWidget * label, char *str, ...)
+{
+	va_list args;
+	gchar *tempstr;
+
+	va_start(args, str);
+	tempstr = g_strdup_vprintf(str, args);
+	va_end(args);
+
+	gtk_label_set_text(GTK_LABEL(label), tempstr);
+	g_free(tempstr);
+}
+
+static void set_entry_tag(GtkEntry * entry, const char * utf8)
+{
+	if(utf8) {
+		if(flac_cfg.title.convert_char_set) {
+			char *text = convert_from_utf8_to_user(utf8);
+			gtk_entry_set_text(entry, text);
+			free(text);
+		}
+		else
+			gtk_entry_set_text(entry, utf8);
+	}
+	else
+		gtk_entry_set_text(entry, "");
+}
+
+static void get_entry_tag(GtkEntry * entry, const char *name)
+{
+	gchar *text;
+	char *utf8;
+
+	text = g_strdup(gtk_entry_get_text(entry));
+	if (!text || strlen(text) == 0) 
+	{
+		g_free(text);
+		return;
+	}
+	if(flac_cfg.title.convert_char_set)
+		utf8 = convert_from_user_to_utf8(text);
+	else
+		utf8 = text;
+
+	FLAC_plugin__tags_add_tag_utf8(tags_, name, utf8, /*separator=*/0);
+
+	if(flac_cfg.title.convert_char_set)
+		free(utf8);
+	g_free(text);
+}
+
+static void show_tag()
+{
+	set_entry_tag(GTK_ENTRY(title_entry)                  , FLAC_plugin__tags_get_tag_utf8(tags_, "TITLE"));
+	set_entry_tag(GTK_ENTRY(artist_entry)                 , FLAC_plugin__tags_get_tag_utf8(tags_, "ARTIST"));
+	set_entry_tag(GTK_ENTRY(album_entry)                  , FLAC_plugin__tags_get_tag_utf8(tags_, "ALBUM"));
+	set_entry_tag(GTK_ENTRY(date_entry)                   , FLAC_plugin__tags_get_tag_utf8(tags_, "DATE"));
+	set_entry_tag(GTK_ENTRY(tracknum_entry)               , FLAC_plugin__tags_get_tag_utf8(tags_, "TRACKNUMBER"));
+	set_entry_tag(GTK_ENTRY(comment_entry)                , FLAC_plugin__tags_get_tag_utf8(tags_, "DESCRIPTION"));
+	set_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), FLAC_plugin__tags_get_tag_utf8(tags_, "GENRE"));
+}
+
+static void save_tag(GtkWidget * w, gpointer data)
+{
+	(void)w;
+	(void)data;
+
+	FLAC_plugin__tags_delete_tag(tags_, "TITLE");
+	FLAC_plugin__tags_delete_tag(tags_, "ARTIST");
+	FLAC_plugin__tags_delete_tag(tags_, "ALBUM");
+	FLAC_plugin__tags_delete_tag(tags_, "DATE");
+	FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER");
+	FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION");
+	FLAC_plugin__tags_delete_tag(tags_, "GENRE");
+
+	get_entry_tag(GTK_ENTRY(title_entry)                  , "TITLE");
+	get_entry_tag(GTK_ENTRY(artist_entry)                 , "ARTIST");
+	get_entry_tag(GTK_ENTRY(album_entry)                  , "ALBUM");
+	get_entry_tag(GTK_ENTRY(date_entry)                   , "DATE");
+	get_entry_tag(GTK_ENTRY(tracknum_entry)               , "TRACKNUMBER");
+	get_entry_tag(GTK_ENTRY(comment_entry)                , "DESCRIPTION");
+	get_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), "GENRE");
+
+	FLAC_plugin__tags_set(current_filename, tags_);
+	gtk_widget_destroy(window);
+}
+
+static void remove_tag(GtkWidget * w, gpointer data)
+{
+	(void)w;
+	(void)data;
+	
+	FLAC_plugin__tags_delete_tag(tags_, "TITLE");
+	FLAC_plugin__tags_delete_tag(tags_, "ARTIST");
+	FLAC_plugin__tags_delete_tag(tags_, "ALBUM");
+	FLAC_plugin__tags_delete_tag(tags_, "DATE");
+	FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER");
+	FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION");
+	FLAC_plugin__tags_delete_tag(tags_, "GENRE");
+
+	FLAC_plugin__tags_set(current_filename, tags_);
+	gtk_widget_destroy(window);
+}
+
+static void show_file_info()
+{
+	FLAC__StreamMetadata streaminfo;
+	struct stat _stat;
+
+	gtk_label_set_text(GTK_LABEL(flac_samplerate), "");
+	gtk_label_set_text(GTK_LABEL(flac_channels), "");
+	gtk_label_set_text(GTK_LABEL(flac_bits_per_sample), "");
+	gtk_label_set_text(GTK_LABEL(flac_blocksize), "");
+	gtk_label_set_text(GTK_LABEL(flac_filesize), "");
+	gtk_label_set_text(GTK_LABEL(flac_samples), "");
+	gtk_label_set_text(GTK_LABEL(flac_bitrate), "");
+
+	if(!FLAC__metadata_get_streaminfo(current_filename, &streaminfo)) {
+		return;
+	}
+
+	label_set_text(flac_samplerate, _("Samplerate: %d Hz"), streaminfo.data.stream_info.sample_rate);
+	label_set_text(flac_channels, _("Channels: %d"), streaminfo.data.stream_info.channels);
+	label_set_text(flac_bits_per_sample, _("Bits/Sample: %d"), streaminfo.data.stream_info.bits_per_sample);
+	if(streaminfo.data.stream_info.min_blocksize == streaminfo.data.stream_info.max_blocksize)
+		label_set_text(flac_blocksize, _("Blocksize: %d"), streaminfo.data.stream_info.min_blocksize);
+	else
+		label_set_text(flac_blocksize, _("Blocksize: variable\n  min/max: %d/%d"), streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize);
+
+	if (streaminfo.data.stream_info.total_samples)
+		label_set_text(flac_samples, _("Samples: %llu\nLength: %d:%.2d"),
+				streaminfo.data.stream_info.total_samples,
+				(int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate / 60),
+				(int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate % 60));
+
+	if(!stat(current_filename, &_stat) && S_ISREG(_stat.st_mode)) {
+		label_set_text(flac_filesize, _("Filesize: %ld B"), _stat.st_size);
+		if (streaminfo.data.stream_info.total_samples)
+			label_set_text(flac_bitrate, _("Avg. bitrate: %.1f kb/s\nCompression ratio: %.1f%%"),
+					8.0 * (float)(_stat.st_size) / (1000.0 * (float)streaminfo.data.stream_info.total_samples / (float)streaminfo.data.stream_info.sample_rate),
+					100.0 * (float)_stat.st_size / (float)(streaminfo.data.stream_info.bits_per_sample / 8 * streaminfo.data.stream_info.channels * streaminfo.data.stream_info.total_samples));
+	}
+}
+
+void FLAC_XMMS__file_info_box(char *filename)
+{
+	unsigned i;
+	gchar *title;
+	gchar *filename_utf8;
+
+	if (!window)
+	{
+		GtkWidget *vbox, *hbox, *left_vbox, *table;
+		GtkWidget *flac_frame, *flac_box;
+		GtkWidget *label, *filename_hbox;
+		GtkWidget *bbox, *save, *remove, *cancel;
+
+		window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+		gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
+		gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window);
+		gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+		vbox = gtk_vbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(window), vbox);
+
+		filename_hbox = gtk_hbox_new(FALSE, 5);
+		gtk_box_pack_start(GTK_BOX(vbox), filename_hbox, FALSE, TRUE, 0);
+
+		label = gtk_label_new(_("Filename:"));
+		gtk_box_pack_start(GTK_BOX(filename_hbox), label, FALSE, TRUE, 0);
+		filename_entry = gtk_entry_new();
+		gtk_editable_set_editable(GTK_EDITABLE(filename_entry), FALSE);
+		gtk_box_pack_start(GTK_BOX(filename_hbox), filename_entry, TRUE, TRUE, 0);
+
+		hbox = gtk_hbox_new(FALSE, 10);
+		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+
+		left_vbox = gtk_vbox_new(FALSE, 10);
+		gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0);
+
+		tag_frame = gtk_frame_new(_("Tag:"));
+		gtk_box_pack_start(GTK_BOX(left_vbox), tag_frame, FALSE, FALSE, 0);
+
+		table = gtk_table_new(5, 5, FALSE);
+		gtk_container_set_border_width(GTK_CONTAINER(table), 5);
+		gtk_container_add(GTK_CONTAINER(tag_frame), table);
+
+		label = gtk_label_new(_("Title:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
+
+		title_entry = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(table), title_entry, 1, 4, 0, 1, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Artist:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
+
+		artist_entry = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(table), artist_entry, 1, 4, 1, 2, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Album:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 5, 5);
+
+		album_entry = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(table), album_entry, 1, 4, 2, 3, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Comment:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 5, 5);
+
+		comment_entry = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(table), comment_entry, 1, 4, 3, 4, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Date:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 5, 5);
+
+		date_entry = gtk_entry_new();
+		gtk_widget_set_usize(date_entry, 40, -1);
+		gtk_table_attach(GTK_TABLE(table), date_entry, 1, 2, 4, 5, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Track number:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5, GTK_FILL, GTK_FILL, 5, 5);
+
+		tracknum_entry = gtk_entry_new();
+		gtk_widget_set_usize(tracknum_entry, 40, -1);
+		gtk_table_attach(GTK_TABLE(table), tracknum_entry, 3, 4, 4, 5, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		label = gtk_label_new(_("Genre:"));
+		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6, GTK_FILL, GTK_FILL, 5, 5);
+
+		genre_combo = gtk_combo_new();
+		gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), TRUE);
+
+		if (!genre_list)
+		{
+			for (i = 0; i < sizeof(vorbis_genres) / sizeof(*vorbis_genres) ; i++)
+				genre_list = g_list_prepend(genre_list, (char *)vorbis_genres[i]);
+			genre_list = g_list_prepend(genre_list, "");
+			genre_list = g_list_sort(genre_list, (GCompareFunc)g_strcasecmp);
+		}
+		gtk_combo_set_popdown_strings(GTK_COMBO(genre_combo), genre_list);
+
+		gtk_table_attach(GTK_TABLE(table), genre_combo, 1, 4, 5, 6, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
+
+		bbox = gtk_hbutton_box_new();
+		gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+		gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+		gtk_box_pack_start(GTK_BOX(left_vbox), bbox, FALSE, FALSE, 0);
+
+		save = gtk_button_new_with_label(_("Save"));
+		gtk_signal_connect(GTK_OBJECT(save), "clicked", GTK_SIGNAL_FUNC(save_tag), NULL);
+		GTK_WIDGET_SET_FLAGS(save, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0);
+		gtk_widget_grab_default(save);
+
+		remove= gtk_button_new_with_label(_("Remove Tag"));
+		gtk_signal_connect(GTK_OBJECT(remove), "clicked", GTK_SIGNAL_FUNC(remove_tag), NULL);
+		GTK_WIDGET_SET_FLAGS(remove, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbox), remove, TRUE, TRUE, 0);
+
+		cancel = gtk_button_new_with_label(_("Cancel"));
+		gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));
+		GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+		flac_frame = gtk_frame_new(_("FLAC Info:"));
+		gtk_box_pack_start(GTK_BOX(hbox), flac_frame, FALSE, FALSE, 0);
+
+		flac_box = gtk_vbox_new(FALSE, 5);
+		gtk_container_add(GTK_CONTAINER(flac_frame), flac_box);
+		gtk_container_set_border_width(GTK_CONTAINER(flac_box), 10);
+		gtk_box_set_spacing(GTK_BOX(flac_box), 0);
+
+		flac_samplerate = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_samplerate), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_samplerate), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_samplerate, FALSE, FALSE, 0);
+
+		flac_channels = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_channels), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_channels), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_channels, FALSE, FALSE, 0);
+
+		flac_bits_per_sample = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_bits_per_sample), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_bits_per_sample), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_bits_per_sample, FALSE, FALSE, 0);
+
+		flac_blocksize = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_blocksize), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_blocksize), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_blocksize, FALSE, FALSE, 0);
+
+		flac_filesize = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_filesize), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_filesize), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_filesize, FALSE, FALSE, 0);
+
+		flac_samples = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_samples), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_samples), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_samples, FALSE, FALSE, 0);
+
+		flac_bitrate = gtk_label_new("");
+		gtk_misc_set_alignment(GTK_MISC(flac_bitrate), 0, 0);
+		gtk_label_set_justify(GTK_LABEL(flac_bitrate), GTK_JUSTIFY_LEFT);
+		gtk_box_pack_start(GTK_BOX(flac_box), flac_bitrate, FALSE, FALSE, 0);
+
+		gtk_widget_show_all(window);
+	}
+
+	if(current_filename)
+		g_free(current_filename);
+	if(!(current_filename = g_strdup(filename)))
+		return;
+
+	filename_utf8 = filename_to_utf8(current_filename);
+	title = g_strdup_printf(_("File Info - %s"), g_basename(filename_utf8));
+	gtk_window_set_title(GTK_WINDOW(window), title);
+	g_free(title);
+
+	gtk_entry_set_text(GTK_ENTRY(filename_entry), filename_utf8);
+	gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1);
+
+	g_free(filename_utf8);
+
+	if(tags_)
+		FLAC_plugin__tags_destroy(&tags_);
+
+	FLAC_plugin__tags_get(current_filename, &tags_);
+
+	show_tag();
+	show_file_info();
+
+	gtk_widget_set_sensitive(tag_frame, TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/grabbag.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,28 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 SHARE__GRABBAG_H
+#define SHARE__GRABBAG_H
+
+/* These can't be included by themselves, only from within grabbag.h */
+#include "grabbag/cuesheet.h"
+#include "grabbag/file.h"
+#include "grabbag/replaygain.h"
+#include "grabbag/seektable.h"
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/grabbag/cuesheet.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,42 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+
+/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */
+
+#ifndef GRABBAG__CUESHEET_H
+#define GRABBAG__CUESHEET_H
+
+#include <stdio.h>
+#include "FLAC/metadata.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames);
+void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames);
+
+FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
+
+void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/grabbag/file.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,54 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+
+/* Convenience routines for manipulating files */
+
+/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */
+
+#ifndef GRABAG__FILE_H
+#define GRABAG__FILE_H
+
+#include <sys/types.h> /* for off_t */
+#include <stdio.h> /* for FILE */
+#include "FLAC/ordinals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grabbag__file_copy_metadata(const char *srcpath, const char *destpath);
+off_t grabbag__file_get_filesize(const char *srcpath);
+const char *grabbag__file_get_basename(const char *srcpath);
+
+/* read_only == false means "make file writable by user"
+ * read_only == true means "make file read-only for everyone"
+ */
+FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only);
+
+/* attempts to make writable before unlinking */
+FLAC__bool grabbag__file_remove_file(const char *filename);
+
+/* these will forcibly set stdin/stdout to binary mode (for OSes that require it) */
+FILE *grabbag__file_get_binary_stdin();
+FILE *grabbag__file_get_binary_stdout();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/grabbag/replaygain.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,64 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+
+/*
+ * This wraps the replaygain_analysis lib, which is LGPL.  This wrapper
+ * allows analysis of different input resolutions by automatically
+ * scaling the input signal
+ */
+
+/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */
+
+#ifndef GRABBAG__REPLAYGAIN_H
+#define GRABBAG__REPLAYGAIN_H
+
+#include "FLAC/metadata.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED;
+
+FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency);
+
+FLAC__bool grabbag__replaygain_init(unsigned sample_frequency);
+
+/* 'bps' must be valid for FLAC, i.e. >=4 and <= 32 */
+FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples);
+
+void grabbag__replaygain_get_album(float *gain, float *peak);
+void grabbag__replaygain_get_title(float *gain, float *peak);
+
+/* These three functions return an error string on error, or NULL if successful */
+const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak);
+const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak);
+const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak);
+const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak);
+const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime);
+const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime);
+const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime);
+
+FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, double *gain, double *peak);
+double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/grabbag/seektable.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,38 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+
+/* Convenience routines for working with seek tables */
+
+/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */
+
+#ifndef GRABAG__SEEKTABLE_H
+#define GRABAG__SEEKTABLE_H
+
+#include "FLAC/format.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+FLAC__bool grabbag__seektable_convert_specification_to_template(const char *spec, FLAC__bool only_explicit_placeholders, FLAC__uint64 total_samples_to_encode, unsigned sample_rate, FLAC__StreamMetadata *seektable_template, FLAC__bool *spec_has_real_points);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/http.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,897 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  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.
+ */
+/* modified for FLAC support by Steven Richman (2003) */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <glib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <audacious/plugin.h>
+#include <audacious/util.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "configure.h"
+#include "plugin_common/locale_hack.h"
+#include "FLAC/format.h"
+#include "plugin.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z))
+#define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w))
+
+static gchar *icy_name = NULL;
+static gint icy_metaint = 0;
+
+extern InputPlugin flac_ip;
+
+#undef DEBUG_UDP
+
+/* Static udp channel functions */
+static int udp_establish_listener (gint *sock);
+static int udp_check_for_data(gint sock);
+
+static char *flac_http_get_title(char *url);
+
+static gboolean prebuffering, going, eof = FALSE;
+static gint sock, rd_index, wr_index, buffer_length, prebuffer_length;
+static guint64 buffer_read = 0;
+static gchar *buffer;
+static guint64 offset;
+static GThread *thread;
+static GtkWidget *error_dialog = NULL;
+
+static FILE *output_file = NULL;
+
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+
+/* Encode the string S of length LENGTH to base64 format and place it
+   to STORE.  STORE will be 0-terminated, and must point to a writable
+   buffer of at least 1+BASE64_LENGTH(length) bytes.  */
+static void base64_encode (const gchar *s, gchar *store, gint length)
+{
+	/* Conversion table.  */
+	static gchar tbl[64] = {
+		'A','B','C','D','E','F','G','H',
+		'I','J','K','L','M','N','O','P',
+		'Q','R','S','T','U','V','W','X',
+		'Y','Z','a','b','c','d','e','f',
+		'g','h','i','j','k','l','m','n',
+		'o','p','q','r','s','t','u','v',
+		'w','x','y','z','0','1','2','3',
+		'4','5','6','7','8','9','+','/'
+	};
+	gint i;
+	guchar *p = (guchar *)store;
+
+	/* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
+	for (i = 0; i < length; i += 3)
+	{
+		*p++ = tbl[s[0] >> 2];
+		*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+		*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
+		*p++ = tbl[s[2] & 0x3f];
+		s += 3;
+	}
+	/* Pad the result if necessary...  */
+	if (i == length + 1)
+		*(p - 1) = '=';
+	else if (i == length + 2)
+		*(p - 1) = *(p - 2) = '=';
+	/* ...and zero-terminate it.  */
+	*p = '\0';
+}
+
+/* Create the authentication header contents for the `Basic' scheme.
+   This is done by encoding the string `USER:PASS' in base64 and
+   prepending `HEADER: Basic ' to it.  */
+static gchar *basic_authentication_encode (const gchar *user, const gchar *passwd, const gchar *header)
+{
+	gchar *t1, *t2, *res;
+	gint len1 = strlen (user) + 1 + strlen (passwd);
+	gint len2 = BASE64_LENGTH (len1);
+
+	t1 = g_strdup_printf("%s:%s", user, passwd);
+	t2 = g_malloc0(len2 + 1);
+	base64_encode (t1, t2, len1);
+	res = g_strdup_printf("%s: Basic %s\r\n", header, t2);
+	g_free(t2);
+	g_free(t1);
+	
+	return res;
+}
+
+static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename)
+{
+	gchar *h, *p, *pt, *f, *temp, *ptr;
+
+	temp = g_strdup(url);
+	ptr = temp;
+
+	if (!strncasecmp("http://", ptr, 7))
+		ptr += 7;
+	h = strchr(ptr, '@');
+	f = strchr(ptr, '/');
+	if (h != NULL && (!f || h < f))
+	{
+		*h = '\0';
+		p = strchr(ptr, ':');
+		if (p != NULL && p < h)
+		{
+			*p = '\0';
+			p++;
+			*pass = g_strdup(p);
+		}
+		else
+			*pass = NULL;
+		*user = g_strdup(ptr);
+		h++;
+		ptr = h;
+	}
+	else
+	{
+		*user = NULL;
+		*pass = NULL;
+		h = ptr;
+	}
+	pt = strchr(ptr, ':');
+	if (pt != NULL && (f == NULL || pt < f))
+	{
+		*pt = '\0';
+		*port = atoi(pt + 1);
+	}
+	else
+	{
+		if (f)
+			*f = '\0';
+		*port = 80;
+	}
+	*host = g_strdup(h);
+	
+	if (f)
+		*filename = g_strdup(f + 1);
+	else
+		*filename = NULL;
+	g_free(temp);
+}
+
+void flac_http_close(void)
+{
+	going = FALSE;
+
+	g_thread_join(thread);
+	g_free(icy_name);
+	icy_name = NULL;
+}
+
+
+static gint http_used(void)
+{
+	if (wr_index >= rd_index)
+		return wr_index - rd_index;
+	return buffer_length - (rd_index - wr_index);
+}
+
+static gint http_free(void)
+{
+	if (rd_index > wr_index)
+		return (rd_index - wr_index) - 1;
+	return (buffer_length - (wr_index - rd_index)) - 1;
+}
+
+static void http_wait_for_data(gint bytes)
+{
+	while ((prebuffering || http_used() < bytes) && !eof && going)
+		xmms_usleep(10000);
+}
+
+static void show_error_message(gchar *error)
+{
+	if(!error_dialog)
+	{
+		GDK_THREADS_ENTER();
+		error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE,
+						 NULL, NULL);
+		g_signal_connect(G_OBJECT(error_dialog),
+				   "destroy",
+				   G_CALLBACK(gtk_widget_destroyed),
+				   &error_dialog);
+		GDK_THREADS_LEAVE();
+	}
+}
+
+int flac_http_read(gpointer data, gint length)
+{
+	gint len, cnt, off = 0, meta_len, meta_off = 0, i;
+	gchar *meta_data, **tags, *temp, *title;
+	if (length > buffer_length) {
+		length = buffer_length;
+	}
+
+	http_wait_for_data(length);
+
+	if (!going)
+		return 0;
+	len = min(http_used(), length);
+
+	while (len && http_used())
+	{
+		if ((flac_cfg.stream.cast_title_streaming) && (icy_metaint > 0) && (buffer_read % icy_metaint) == 0 && (buffer_read > 0))
+		{
+			meta_len = *((guchar *) buffer + rd_index) * 16;
+			rd_index = (rd_index + 1) % buffer_length;
+			if (meta_len > 0)
+			{
+				http_wait_for_data(meta_len);
+				meta_data = g_malloc0(meta_len);
+				if (http_used() >= meta_len)
+				{
+					while (meta_len)
+					{
+						cnt = min(meta_len, buffer_length - rd_index);
+						memcpy(meta_data + meta_off, buffer + rd_index, cnt);
+						rd_index = (rd_index + cnt) % buffer_length;
+						meta_len -= cnt;
+						meta_off += cnt;
+					}
+					tags = g_strsplit(meta_data, "';", 0);
+
+					for (i = 0; tags[i]; i++)
+					{
+						if (!strncasecmp(tags[i], "StreamTitle=", 12))
+						{
+							temp = g_strdup(tags[i] + 13);
+							title = g_strdup_printf("%s (%s)", temp, icy_name);
+							set_track_info(title, -1);
+							g_free(title);
+							g_free(temp);
+						}
+
+					}
+					g_strfreev(tags);
+
+				}
+				g_free(meta_data);
+			}
+			if (!http_used())
+				http_wait_for_data(length - off);
+			cnt = min3(len, buffer_length - rd_index, http_used());
+		}
+		else if ((icy_metaint > 0) && (flac_cfg.stream.cast_title_streaming))
+			cnt = min4(len, buffer_length - rd_index, http_used(), icy_metaint - (gint) (buffer_read % icy_metaint));
+		else
+			cnt = min3(len, buffer_length - rd_index, http_used());
+		if (output_file)
+			fwrite(buffer + rd_index, 1, cnt, output_file);
+
+		memcpy((gchar *)data + off, buffer + rd_index, cnt);
+		rd_index = (rd_index + cnt) % buffer_length;
+		buffer_read += cnt;
+		len -= cnt;
+		off += cnt;
+	}
+	if (!off) {
+		fprintf(stderr, "returning zero\n");
+	}
+	return off;
+}
+
+static gboolean http_check_for_data(void)
+{
+
+	fd_set set;
+	struct timeval tv;
+	gint ret;
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 20000;
+	FD_ZERO(&set);
+	FD_SET(sock, &set);
+	ret = select(sock + 1, &set, NULL, NULL, &tv);
+	if (ret > 0)
+		return TRUE;
+	return FALSE;
+}
+
+gint flac_http_read_line(gchar * buf, gint size)
+{
+	gint i = 0;
+
+	while (going && i < size - 1)
+	{
+		if (http_check_for_data())
+		{
+			if (read(sock, buf + i, 1) <= 0)
+				return -1;
+			if (buf[i] == '\n')
+				break;
+			if (buf[i] != '\r')
+				i++;
+		}
+	}
+	if (!going)
+		return -1;
+	buf[i] = '\0';
+	return i;
+}
+
+/* returns the file descriptor of the socket, or -1 on error */
+static int http_connect (gchar *url_, gboolean head, guint64 offset)
+{
+	gchar line[1024], *user, *pass, *host, *filename,
+	     *status, *url, *temp, *file;
+	gchar *chost;
+	gint cnt, error, port, cport;
+	guint err_len;
+	gboolean redirect;
+	int udp_sock = 0;
+	fd_set set;
+	struct hostent *hp;
+	struct sockaddr_in address;
+	struct timeval tv;
+
+	url = g_strdup (url_);
+
+	do
+	{
+		redirect=FALSE;
+	
+		g_strstrip(url);
+
+		parse_url(url, &user, &pass, &host, &port, &filename);
+
+		if ((!filename || !*filename) && url[strlen(url) - 1] != '/')
+			temp = g_strconcat(url, "/", NULL);
+		else
+			temp = g_strdup(url);
+		g_free(url);
+		url = temp;
+
+		chost = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_host : host;
+		cport = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_port : port;
+
+		sock = socket(AF_INET, SOCK_STREAM, 0);
+		fcntl(sock, F_SETFL, O_NONBLOCK);
+		address.sin_family = AF_INET;
+
+		status = g_strdup_printf(_("LOOKING UP %s"), chost);
+		flac_ip.set_info_text(status);
+		g_free(status);
+
+		if (!(hp = gethostbyname(chost)))
+		{
+			status = g_strdup_printf(_("Couldn't look up host %s"), chost);
+			show_error_message(status);
+			g_free(status);
+
+			flac_ip.set_info_text(NULL);
+			eof = TRUE;
+		}
+
+		if (!eof)
+		{
+			memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list), sizeof (address.sin_addr.s_addr));
+			address.sin_port = (gint) g_htons(cport);
+
+			status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport);
+			flac_ip.set_info_text(status);
+			g_free(status);
+			if (connect(sock, (struct sockaddr *) &address, sizeof (struct sockaddr_in)) == -1)
+			{
+				if (errno != EINPROGRESS)
+				{
+					status = g_strdup_printf(_("Couldn't connect to host %s"), chost);
+					show_error_message(status);
+					g_free(status);
+
+					flac_ip.set_info_text(NULL);
+					eof = TRUE;
+				}
+			}
+			while (going)
+			{
+				tv.tv_sec = 0;
+				tv.tv_usec = 10000;
+				FD_ZERO(&set);
+				FD_SET(sock, &set);
+				if (select(sock + 1, NULL, &set, NULL, &tv) > 0)
+				{
+					err_len = sizeof (error);
+					getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len);
+					if (error)
+					{
+						status = g_strdup_printf(_("Couldn't connect to host %s"),
+									 chost);
+						show_error_message(status);
+						g_free(status);
+
+						flac_ip.set_info_text(NULL);
+						eof = TRUE;
+						
+					}
+					break;
+				}
+			}
+			if (!eof)
+			{
+				gchar *auth = NULL, *proxy_auth = NULL;
+				gchar udpspace[30];
+				int udp_port;
+
+				if (flac_cfg.stream.use_udp_channel)
+				{
+					udp_port = udp_establish_listener (&udp_sock);
+					if (udp_port > 0) 
+						sprintf (udpspace, "x-audiocast-udpport: %d\r\n", udp_port);
+					else
+						udp_sock = 0;
+				}
+					
+				if(user && pass)
+					auth = basic_authentication_encode(user, pass, "Authorization");
+
+				if (flac_cfg.stream.use_proxy)
+				{
+					file = g_strdup(url);
+					if(flac_cfg.stream.proxy_use_auth && flac_cfg.stream.proxy_user && flac_cfg.stream.proxy_pass)
+					{
+						proxy_auth = basic_authentication_encode(flac_cfg.stream.proxy_user,
+											 flac_cfg.stream.proxy_pass,
+											 "Proxy-Authorization");
+					}
+				}
+				else
+					file = g_strconcat("/", filename, NULL);
+
+				temp = g_strdup_printf("GET %s HTTP/1.0\r\n"
+						       "Host: %s\r\n"
+						       "User-Agent: %s/%s\r\n"
+						       "%s%s%s%s",
+						       file, host, "Reference FLAC Player", FLAC__VERSION_STRING, 
+						       proxy_auth ? proxy_auth : "", auth ? auth : "",
+						       flac_cfg.stream.cast_title_streaming ?  "Icy-MetaData:1\r\n" : "",
+						       flac_cfg.stream.use_udp_channel ? udpspace : "");
+				if (offset && !head) {
+					gchar *temp_dead = temp;
+					temp = g_strconcat ("%sRange: %ll-\r\n", temp, offset, NULL);
+					fprintf (stderr, "%s", temp);
+					g_free (temp_dead);
+				}
+				
+				g_free(file);
+				if(proxy_auth)
+					g_free(proxy_auth);
+				if(auth)
+					g_free(auth);
+				write(sock, temp, strlen(temp));
+				write(sock, "\r\n", 2);
+				g_free(temp);
+				flac_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY"));
+				while (going && !eof)
+				  {
+					if (http_check_for_data())
+					{
+						if (flac_http_read_line(line, 1024))
+						{
+							status = strchr(line, ' ');
+							if (status)
+							{
+								if (status[1] == '2')
+									break;
+								else if(status[1] == '3' && status[2] == '0' && status[3] == '2')
+								{
+									while(going)
+									{
+										if(http_check_for_data())
+										{
+											if((cnt = flac_http_read_line(line, 1024)) != -1)
+											{
+												if(!cnt)
+													break;
+												if(!strncmp(line, "Location:", 9))
+												{
+													g_free(url);
+													url = g_strdup(line+10);
+												}
+											}
+											else
+											{
+												eof=TRUE;
+												flac_ip.set_info_text(NULL);
+												break;
+											}
+										}
+									}			
+									redirect=TRUE;
+									break;
+								}
+								else
+								{
+									status = g_strdup_printf(_("Couldn't connect to host %s\nServer reported: %s"), chost, status);
+									show_error_message(status);
+									g_free(status);
+									break;
+								}
+							}
+						}
+						else
+						{
+							eof = TRUE;
+							flac_ip.set_info_text(NULL);
+						}
+					}
+				}
+
+				while (going && !redirect)
+				{
+					if (http_check_for_data())
+					{
+						if ((cnt = flac_http_read_line(line, 1024)) != -1)
+						{
+							if (!cnt)
+								break;
+							if (!strncmp(line, "icy-name:", 9))
+								icy_name = g_strdup(line + 9);
+							else if (!strncmp(line, "x-audiocast-name:", 17))
+								icy_name = g_strdup(line + 17);
+							if (!strncmp(line, "icy-metaint:", 12))
+								icy_metaint = atoi(line + 12);
+							if (!strncmp(line, "x-audiocast-udpport:", 20)) {
+#ifdef DEBUG_UDP
+								fprintf (stderr, "Server wants udp messages on port %d\n", atoi (line + 20));
+#endif
+								/*udp_serverport = atoi (line + 20);*/
+							}
+							
+						}
+						else
+						{
+							eof = TRUE;
+							flac_ip.set_info_text(NULL);
+							break;
+						}
+					}
+				}
+			}
+		}
+	
+		if(redirect)
+		{
+			if (output_file)
+			{
+				fclose(output_file);
+				output_file = NULL;
+			}
+			close(sock);
+		}
+
+		g_free(user);
+		g_free(pass);
+		g_free(host);
+		g_free(filename);
+	} while(redirect);
+
+	g_free(url);
+	return eof ? -1 : sock;
+}
+
+static void *http_buffer_loop(void *arg)
+{
+	gchar *status, *url, *temp, *file;
+	gint cnt, written;
+	int udp_sock = 0;
+
+	url = (gchar *) arg;
+	sock = http_connect (url, false, offset);
+	
+	if (sock >= 0 && flac_cfg.stream.save_http_stream) {
+		gchar *output_name;
+		file = flac_http_get_title(url);
+		output_name = file;
+		if (!strncasecmp(output_name, "http://", 7))
+			output_name += 7;
+		temp = strrchr(output_name, '.');
+		if (temp && (!strcasecmp(temp, ".fla") || !strcasecmp(temp, ".flac")))
+			*temp = '\0';
+
+		while ((temp = strchr(output_name, '/')))
+			*temp = '_';
+		output_name = g_strdup_printf("%s/%s.flac", flac_cfg.stream.save_http_path, output_name);
+
+		g_free(file);
+
+		output_file = fopen(output_name, "wb");
+		g_free(output_name);
+	}
+
+	while (going)
+	{
+
+		if (!http_used() && !flac_ip.output->buffer_playing())
+		{
+			prebuffering = TRUE;
+			flac_ip.set_status_buffering(TRUE);
+		}
+		if (http_free() > 0 && !eof)
+		{
+			if (http_check_for_data())
+			{
+				cnt = min(http_free(), buffer_length - wr_index);
+				if (cnt > 1024)
+					cnt = 1024;
+				written = read(sock, buffer + wr_index, cnt);
+				if (written <= 0)
+				{
+					eof = TRUE;
+					if (prebuffering)
+					{
+						prebuffering = FALSE;
+						flac_ip.set_status_buffering(FALSE);
+
+						flac_ip.set_info_text(NULL);
+					}
+
+				}
+				else
+					wr_index = (wr_index + written) % buffer_length;
+			}
+
+			if (prebuffering)
+			{
+				if (http_used() > prebuffer_length)
+				{
+					prebuffering = FALSE;
+					flac_ip.set_status_buffering(FALSE);
+					flac_ip.set_info_text(NULL);
+				}
+				else
+				{
+					status = g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"), http_used() / 1024, prebuffer_length / 1024);
+					flac_ip.set_info_text(status);
+					g_free(status);
+				}
+
+			}
+		}
+		else
+			xmms_usleep(10000);
+
+		if (flac_cfg.stream.use_udp_channel && udp_sock != 0)
+			if (udp_check_for_data(udp_sock) < 0)
+			{
+				close(udp_sock);
+				udp_sock = 0;
+			}
+	}
+	if (output_file)
+	{
+		fclose(output_file);
+		output_file = NULL;
+	}
+	if (sock >= 0) {
+		close(sock);
+	}
+	if (udp_sock != 0)
+		close(udp_sock);
+
+	g_free(buffer);
+	g_free(url);
+	
+	g_thread_exit(NULL);
+	return NULL; /* avoid compiler warning */
+}
+
+int flac_http_open(gchar * _url, guint64 _offset)
+{
+	gchar *url;
+
+	url = g_strdup(_url);
+
+	rd_index = 0;
+	wr_index = 0;
+	buffer_length = flac_cfg.stream.http_buffer_size * 1024;
+	prebuffer_length = (buffer_length * flac_cfg.stream.http_prebuffer) / 100;
+	buffer_read = 0;
+	icy_metaint = 0;
+	prebuffering = TRUE;
+	flac_ip.set_status_buffering(TRUE);
+	going = TRUE;
+	eof = FALSE;
+	buffer = g_malloc(buffer_length);
+	offset = _offset;
+
+	thread = g_thread_create((GThreadFunc)http_buffer_loop, url, TRUE, NULL);
+
+	return 0;
+}
+
+char *flac_http_get_title(char *url)
+{
+	if (icy_name)
+		return g_strdup(icy_name);
+	if (g_basename(url) && strlen(g_basename(url)) > 0)
+		return g_strdup(g_basename(url));
+	return g_strdup(url);
+}
+
+/* Start UDP Channel specific stuff */
+
+/* Find a good local udp port and bind udp_sock to it, return the port */
+static int udp_establish_listener(int *sock)
+{
+	struct sockaddr_in sin;
+	socklen_t sinlen = sizeof (struct sockaddr_in);
+	
+#ifdef DEBUG_UDP
+	fprintf (stderr,"Establishing udp listener\n");
+#endif
+	
+	if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+	{
+		g_log(NULL, G_LOG_LEVEL_CRITICAL,
+		      "udp_establish_listener(): unable to create socket");
+		return -1;
+	}
+
+	memset(&sin, 0, sinlen);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = g_htonl(INADDR_ANY);
+			
+	if (bind(*sock, (struct sockaddr *)&sin, sinlen) < 0)
+	{
+		g_log(NULL, G_LOG_LEVEL_CRITICAL,
+		      "udp_establish_listener():  Failed to bind socket to localhost: %s", strerror(errno));
+		close(*sock);
+		return -1;
+	}
+	if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0)
+	{
+		g_log(NULL, G_LOG_LEVEL_CRITICAL,
+		      "udp_establish_listener():  Failed to set flags: %s", strerror(errno));
+		close(*sock);
+		return -1;
+	}
+
+	memset(&sin, 0, sinlen);
+	if (getsockname(*sock, (struct sockaddr *)&sin, &sinlen) < 0)
+	{
+		g_log(NULL, G_LOG_LEVEL_CRITICAL,
+		      "udp_establish_listener():  Failed to retrieve socket info: %s", strerror(errno));
+		close(*sock);
+		return -1;
+	}
+
+#ifdef DEBUG_UDP
+	fprintf (stderr,"Listening on local %s:%d\n", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
+#endif
+	
+	return g_ntohs(sin.sin_port);
+}
+
+static int udp_check_for_data(int sock)
+{
+	char buf[1025], **lines;
+	char *valptr;
+	gchar *title;
+	gint len, i;
+	struct sockaddr_in from;
+	socklen_t fromlen;
+
+	fromlen = sizeof(struct sockaddr_in);
+	
+	if ((len = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen)) < 0)
+	{
+		if (errno != EAGAIN)
+		{
+			g_log(NULL, G_LOG_LEVEL_CRITICAL,
+			      "udp_read_data(): Error reading from socket: %s", strerror(errno));
+			return -1;
+		}
+		return 0;
+	}
+	buf[len] = '\0';
+#ifdef DEBUG_UDP
+	fprintf (stderr,"Received: [%s]\n", buf);
+#endif
+	lines = g_strsplit(buf, "\n", 0);
+	if (!lines)
+		return 0;
+	
+	for (i = 0; lines[i]; i++)
+	{
+		while ((lines[i][strlen(lines[i]) - 1] == '\n') ||
+		       (lines[i][strlen(lines[i]) - 1] == '\r'))
+			lines[i][strlen(lines[i]) - 1] = '\0';
+		
+		valptr = strchr(lines[i], ':');
+		
+		if (!valptr)
+			continue;
+		else
+			valptr++;
+		
+		g_strstrip(valptr);
+		if (!strlen(valptr))
+			continue;
+		
+		if (strstr(lines[i], "x-audiocast-streamtitle") != NULL)
+		{
+			title = g_strdup_printf ("%s (%s)", valptr, icy_name);
+			if (going)
+				set_track_info(title, -1);
+			g_free (title);
+		}
+
+#if 0
+		else if (strstr(lines[i], "x-audiocast-streamlength") != NULL)
+		{
+			if (atoi(valptr) != -1)
+				set_track_info(NULL, atoi(valptr));
+		}
+#endif
+				
+		else if (strstr(lines[i], "x-audiocast-streammsg") != NULL)
+		{
+			/* set_track_info(title, -1); */
+/*  			xmms_show_message(_("Message"), valptr, _("Ok"), */
+/*  					  FALSE, NULL, NULL); */
+			g_message("Stream_message: %s", valptr);
+		}
+
+#if 0
+		/* Use this to direct your webbrowser.. yeah right.. */
+		else if (strstr(lines[i], "x-audiocast-streamurl") != NULL)
+		{
+			if (lasturl && g_strcmp (valptr, lasturl))
+			{
+				c_message (stderr, "Song URL: %s\n", valptr);
+				g_free (lasturl);
+				lasturl = g_strdup (valptr);
+			}
+		}
+#endif
+		else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL)
+		{
+			gchar obuf[60];
+			sprintf(obuf, "x-audiocast-ack: %ld \r\n", atol(valptr));
+			if (sendto(sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from, fromlen) < 0)
+			{
+				g_log(NULL, G_LOG_LEVEL_WARNING,
+				      "udp_check_for_data(): Unable to send ack to server: %s", strerror(errno));
+			}
+#ifdef DEBUG_UDP
+			else
+				fprintf(stderr,"Sent ack: %s", obuf);
+			fprintf (stderr,"Remote: %s:%d\n", inet_ntoa(from.sin_addr), g_ntohs(from.sin_port));
+#endif
+		}
+	}
+	g_strfreev(lines);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/http.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,26 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ *
+ * 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 __HTTP_H__
+#define __HTTP_H__
+
+extern int flac_http_open(gchar * url, guint64 offset);
+extern void flac_http_close(void);
+extern int flac_http_read(gpointer data, gint length);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,862 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "audacious/plugin.h"
+#include "audacious/output.h"
+#include "audacious/util.h"
+#include "audacious/configdb.h"
+#include "audacious/titlestring.h"
+#include "audacious/vfs.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "FLAC/all.h"
+#include "plugin_common/all.h"
+#include "grabbag.h"
+#include "replaygain_synthesis.h"
+#include "configure.h"
+#include "charset.h"
+#include "http.h"
+#include "tag.h"
+
+#ifdef min
+#undef min
+#endif
+#define min(x,y) ((x)<(y)?(x):(y))
+
+/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */
+#ifdef _MSC_VER
+#define FLAC__U64L(x) x
+#else
+#define FLAC__U64L(x) x##LLU
+#endif
+
+extern void FLAC_XMMS__file_info_box(char *filename);
+
+typedef struct {
+	FLAC__bool abort_flag;
+	FLAC__bool is_playing;
+	FLAC__bool eof;
+	FLAC__bool play_thread_open; /* if true, is_playing must also be true */
+	unsigned total_samples;
+	unsigned bits_per_sample;
+	unsigned channels;
+	unsigned sample_rate;
+	unsigned length_in_msec;
+	gchar *title;
+	AFormat sample_format;
+	unsigned sample_format_bytes_per_sample;
+	int seek_to_in_sec;
+	FLAC__bool has_replaygain;
+	double replay_scale;
+	DitherContext dither_context;
+} file_info_struct;
+
+typedef FLAC__StreamDecoderWriteStatus (*WriteCallback) (const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+typedef void (*MetadataCallback) (const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+typedef void (*ErrorCallback) (const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+typedef struct {
+	FLAC__bool seekable;
+	void* (*new_decoder) (void);
+	FLAC__bool (*set_md5_checking) (void *decoder, FLAC__bool value);
+	FLAC__bool (*set_source) (void *decoder, const char* source);
+	FLAC__bool (*set_metadata_ignore_all) (void *decoder);
+	FLAC__bool (*set_metadata_respond) (void *decoder, FLAC__MetadataType type);
+	FLAC__bool (*set_write_callback) (void *decoder, WriteCallback value);
+	FLAC__bool (*set_metadata_callback) (void *decoder, MetadataCallback value);
+	FLAC__bool (*set_error_callback) (void *decoder, ErrorCallback value);
+	FLAC__bool (*set_client_data) (void *decoder, void *value);
+	FLAC__bool (*decoder_init) (void *decoder);
+	void (*safe_decoder_finish) (void *decoder);
+	void (*safe_decoder_delete) (void *decoder);
+	FLAC__bool (*process_until_end_of_metadata) (void *decoder);
+	FLAC__bool (*process_single) (void *decoder);
+	FLAC__bool (*is_eof) (void *decoder);
+} decoder_funcs_t;
+
+#define NUM_DECODER_TYPES 2
+typedef enum {
+	DECODER_FILE,
+	DECODER_HTTP
+} decoder_t;
+
+static void FLAC_XMMS__init();
+static int  FLAC_XMMS__is_our_file(char *filename);
+static void FLAC_XMMS__play_file(char *filename);
+static void FLAC_XMMS__stop();
+static void FLAC_XMMS__pause(short p);
+static void FLAC_XMMS__seek(int time);
+static int  FLAC_XMMS__get_time();
+static void FLAC_XMMS__cleanup();
+static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length);
+
+static void *play_loop_(void *arg);
+
+static FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fnsp);
+static void file_decoder_safe_decoder_finish_(void *decoder);
+static void file_decoder_safe_decoder_delete_(void *decoder);
+static FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+static void init_decoder_func_tables();
+static decoder_t source_to_decoder_type (const char *source);
+
+InputPlugin flac_ip =
+{
+	NULL,
+	NULL,
+	NULL,
+	FLAC_XMMS__init,
+	FLAC_XMMS__aboutbox,
+	FLAC_XMMS__configure,
+	FLAC_XMMS__is_our_file,
+	NULL,
+	FLAC_XMMS__play_file,
+	FLAC_XMMS__stop,
+	FLAC_XMMS__pause,
+	FLAC_XMMS__seek,
+	NULL,
+	FLAC_XMMS__get_time,
+	NULL,
+	NULL,
+	FLAC_XMMS__cleanup,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	FLAC_XMMS__get_song_info,
+	FLAC_XMMS__file_info_box,
+	NULL,
+	flac_get_tuple
+};
+
+#define SAMPLES_PER_WRITE 512
+#define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8))
+static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE];
+static unsigned sample_buffer_first_, sample_buffer_last_;
+
+static void *decoder_ = 0, *decoder2 = 0;
+static file_info_struct file_info_;
+static GThread *decode_thread_;
+static FLAC__bool audio_error_ = false;
+static FLAC__bool is_big_endian_host_;
+
+#define BITRATE_HIST_SEGMENT_MSEC 500
+/* 500ms * 50 = 25s should be enough */
+#define BITRATE_HIST_SIZE 50
+static unsigned bitrate_history_[BITRATE_HIST_SIZE];
+
+/* A table of sets of decoder functions, indexed by decoder_t */
+static const decoder_funcs_t* DECODER_FUNCS[NUM_DECODER_TYPES];
+
+static decoder_funcs_t const * decoder_func_table_, * decoder_func_table2;
+
+
+InputPlugin *get_iplugin_info()
+{
+	flac_ip.description = g_strdup_printf(_("FLAC Audio Plugin"));
+	return &flac_ip;
+}
+
+void set_track_info(const char* title, int length_in_msec)
+{
+	if (file_info_.is_playing) {
+		flac_ip.set_info((char*) title, length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels);
+	}
+}
+
+static gchar* homedir()
+{
+	gchar *result;
+	char *env_home = getenv("HOME");
+	if (env_home) {
+		result = g_strdup (env_home);
+	} else {
+		uid_t uid = getuid();
+		struct passwd *pwent;
+		do {
+			pwent = getpwent();
+		} while (pwent && pwent->pw_uid != uid);
+		result = pwent ? g_strdup (pwent->pw_dir) : NULL;
+		endpwent();
+	}
+	return result;
+}
+
+void FLAC_XMMS__init()
+{
+	ConfigDb *db;
+	FLAC__uint32 test = 1;
+	gchar *tmp = NULL;
+
+	is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true;
+
+	memset(&flac_cfg, 0, sizeof(flac_cfg));
+
+	db = bmp_cfg_db_open();
+
+	/* title */
+
+	bmp_cfg_db_get_bool(db, "flac", "title.tag_override", &flac_cfg.title.tag_override);
+
+	if(!bmp_cfg_db_get_string(db, "flac", "title.tag_format", &flac_cfg.title.tag_format))
+		flac_cfg.title.tag_format = g_strdup("%p - %t");
+
+	bmp_cfg_db_get_bool(db, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set);
+
+	if(!bmp_cfg_db_get_string(db, "flac", "title.user_char_set", &flac_cfg.title.user_char_set))
+		flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current();
+
+	/* replaygain */
+
+	bmp_cfg_db_get_bool(db, "flac", "output.replaygain.enable", &flac_cfg.output.replaygain.enable);
+
+	bmp_cfg_db_get_bool(db, "flac", "output.replaygain.album_mode", &flac_cfg.output.replaygain.album_mode);
+
+	if(!bmp_cfg_db_get_int(db, "flac", "output.replaygain.preamp", &flac_cfg.output.replaygain.preamp))
+		flac_cfg.output.replaygain.preamp = 0;
+
+	bmp_cfg_db_get_bool(db, "flac", "output.replaygain.hard_limit", &flac_cfg.output.replaygain.hard_limit);
+
+	bmp_cfg_db_get_bool(db, "flac", "output.resolution.normal.dither_24_to_16", &flac_cfg.output.resolution.normal.dither_24_to_16);
+	bmp_cfg_db_get_bool(db, "flac", "output.resolution.replaygain.dither", &flac_cfg.output.resolution.replaygain.dither);
+
+	if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.noise_shaping", &flac_cfg.output.resolution.replaygain.noise_shaping))
+		flac_cfg.output.resolution.replaygain.noise_shaping = 1;
+
+	if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out))
+		flac_cfg.output.resolution.replaygain.bps_out = 16;
+
+	/* stream */
+
+	bmp_cfg_db_get_int(db, "flac", "stream.http_buffer_size", &flac_cfg.stream.http_buffer_size);
+	bmp_cfg_db_get_int(db, "flac", "stream.http_prebuffer", &flac_cfg.stream.http_prebuffer);
+	bmp_cfg_db_get_bool(db, "flac", "stream.save_http_stream", &flac_cfg.stream.save_http_stream);
+	if (!bmp_cfg_db_get_string(db, "flac", "stream.save_http_path", &flac_cfg.stream.save_http_path) ||
+		 ! *flac_cfg.stream.save_http_path) {
+	  /* TODO: Is this a memory leak ?? */
+	  /*
+		if (flac_cfg.stream.save_http_path)
+			g_free (flac_cfg.stream.save_http_path);
+	  */
+		flac_cfg.stream.save_http_path = homedir();
+	}
+	bmp_cfg_db_get_bool(db, "flac", "stream.cast_title_streaming", &flac_cfg.stream.cast_title_streaming);
+	bmp_cfg_db_get_bool(db, "flac", "stream.use_udp_channel", &flac_cfg.stream.use_udp_channel);
+
+	init_decoder_func_tables();
+ 	decoder_func_table_ = DECODER_FUNCS [DECODER_FILE];
+	decoder_ = decoder_func_table_ -> new_decoder();
+
+	bmp_cfg_db_get_bool(db, NULL, "use_proxy", &flac_cfg.stream.use_proxy);
+	bmp_cfg_db_get_string(db, NULL, "proxy_host", &flac_cfg.stream.proxy_host);
+	bmp_cfg_db_get_string(db, NULL, "proxy_port", &tmp);
+
+	if (tmp != NULL)
+		flac_cfg.stream.proxy_port = atoi(tmp);
+
+	bmp_cfg_db_get_bool(db, NULL, "proxy_use_auth", &flac_cfg.stream.proxy_use_auth);
+	bmp_cfg_db_get_string(db, NULL, "proxy_user", &flac_cfg.stream.proxy_user);
+	bmp_cfg_db_get_string(db, NULL, "proxy_pass", &flac_cfg.stream.proxy_pass);
+
+	bmp_cfg_db_close(db);
+}
+
+int FLAC_XMMS__is_our_file(char *filename)
+{
+	FILE *f;
+	FLAC__StreamMetadata streaminfo;
+
+        if (source_to_decoder_type (filename) == DECODER_FILE) {
+                if(0 == (f = fopen(filename, "r")))
+                        return 0;
+                fclose(f);
+	        if(!FLAC__metadata_get_streaminfo(filename, &streaminfo))
+			return 0;
+		return 1;
+        }
+
+	if(!safe_decoder_init_(filename, &decoder2, &decoder_func_table2))
+		return 0;
+
+	decoder_func_table2 -> safe_decoder_finish(decoder2);	
+	return 1;
+}
+
+void FLAC_XMMS__play_file(char *filename)
+{
+	FILE *f;
+
+	sample_buffer_first_ = sample_buffer_last_ = 0;
+	audio_error_ = false;
+	file_info_.abort_flag = false;
+	file_info_.is_playing = false;
+	file_info_.eof = false;
+	file_info_.play_thread_open = false;
+	file_info_.has_replaygain = false;
+
+	if (source_to_decoder_type (filename) == DECODER_FILE) {
+		if(0 == (f = fopen(filename, "r")))
+			return;
+		fclose(f);
+	}
+
+	if(decoder_ == 0)
+		return;
+
+	if(!safe_decoder_init_(filename, &decoder_, &decoder_func_table_))
+		return;
+
+	if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable) {
+		if(flac_cfg.output.resolution.replaygain.bps_out == 8) {
+			file_info_.sample_format = FMT_U8;
+			file_info_.sample_format_bytes_per_sample = 1;
+		}
+		else if(flac_cfg.output.resolution.replaygain.bps_out == 16) {
+			file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE;
+			file_info_.sample_format_bytes_per_sample = 2;
+		}
+		else {
+			/*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */
+			fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out);
+			decoder_func_table_ -> safe_decoder_finish(decoder_);
+			return;
+		}
+	}
+	else {
+		if(file_info_.bits_per_sample == 8) {
+			file_info_.sample_format = FMT_U8;
+			file_info_.sample_format_bytes_per_sample = 1;
+		}
+		else if(file_info_.bits_per_sample == 16 || (file_info_.bits_per_sample == 24 && flac_cfg.output.resolution.normal.dither_24_to_16)) {
+			file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE;
+			file_info_.sample_format_bytes_per_sample = 2;
+		}
+		else {
+			/*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */
+			fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample);
+			decoder_func_table_ -> safe_decoder_finish(decoder_);
+			return;
+		}
+	}
+	FLAC__replaygain_synthesis__init_dither_context(&file_info_.dither_context, file_info_.sample_format_bytes_per_sample * 8, flac_cfg.output.resolution.replaygain.noise_shaping);
+	file_info_.is_playing = true;
+
+	if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) {
+		audio_error_ = true;
+		decoder_func_table_ -> safe_decoder_finish(decoder_);
+		return;
+	}
+
+	file_info_.title = flac_format_song_title(filename);
+	flac_ip.set_info(file_info_.title, file_info_.length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels);
+
+	file_info_.seek_to_in_sec = -1;
+	file_info_.play_thread_open = true;
+	decode_thread_ = g_thread_create((GThreadFunc)play_loop_, NULL, TRUE, NULL);
+}
+
+void FLAC_XMMS__stop()
+{
+	if(file_info_.is_playing) {
+		file_info_.is_playing = false;
+		if(file_info_.play_thread_open) {
+			file_info_.play_thread_open = false;
+			g_thread_join(decode_thread_);
+		}
+		flac_ip.output->close_audio();
+		decoder_func_table_ -> safe_decoder_finish (decoder_);
+	}
+}
+
+void FLAC_XMMS__pause(short p)
+{
+	flac_ip.output->pause(p);
+}
+
+void FLAC_XMMS__seek(int time)
+{
+	if (decoder_func_table_->seekable) {
+		file_info_.seek_to_in_sec = time;
+		file_info_.eof = false;
+
+		while(file_info_.seek_to_in_sec != -1)
+			xmms_usleep(10000);
+	}
+}
+
+int FLAC_XMMS__get_time()
+{
+	if(audio_error_)
+		return -2;
+	if(!file_info_.is_playing || (file_info_.eof && !flac_ip.output->buffer_playing()))
+		return -1;
+	else
+		return flac_ip.output->output_time();
+}
+
+void FLAC_XMMS__cleanup()
+{
+    g_free(flac_ip.description);
+    flac_ip.description = NULL;
+
+    if (flac_cfg.title.tag_format) {
+        free(flac_cfg.title.tag_format);
+        flac_cfg.title.tag_format = NULL;
+    }
+
+    if (flac_cfg.title.user_char_set) {
+        free(flac_cfg.title.user_char_set);
+        flac_cfg.title.user_char_set = NULL;
+    }
+
+    if (flac_cfg.stream.proxy_host) {
+        free(flac_cfg.stream.proxy_host);
+        flac_cfg.stream.proxy_host = NULL;
+    }
+
+    if (flac_cfg.stream.proxy_user) {
+        free(flac_cfg.stream.proxy_user);
+        flac_cfg.stream.proxy_user = NULL;
+
+    }
+
+    if (flac_cfg.stream.proxy_pass) {
+        free(flac_cfg.stream.proxy_pass);
+        flac_cfg.stream.proxy_pass = NULL;
+    }
+
+	decoder_func_table_ -> safe_decoder_delete(decoder_);
+	decoder_ = 0;
+}
+
+void FLAC_XMMS__get_song_info(char *filename, char **title, int *length_in_msec)
+{
+	FLAC__StreamMetadata streaminfo;
+
+	if(0 == filename)
+		filename = "";
+
+	if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
+		/* @@@ how to report the error? */
+		if(title) {
+			if (source_to_decoder_type (filename) == DECODER_FILE) {
+				static const char *errtitle = "Invalid FLAC File: ";
+				*title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1);
+				sprintf(*title, "%s\"%s\"", errtitle, filename);
+			} else {
+				*title = NULL;
+			}
+		}
+		if(length_in_msec)
+			*length_in_msec = -1;
+		return;
+	}
+
+	if(title) {
+		*title = flac_format_song_title(filename);
+	}
+	if(length_in_msec)
+		*length_in_msec = (unsigned)((double)streaminfo.data.stream_info.total_samples / (double)streaminfo.data.stream_info.sample_rate * 1000.0 + 0.5);
+}
+
+/***********************************************************************
+ * local routines
+ **********************************************************************/
+
+void *play_loop_(void *arg)
+{
+	unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1;
+	FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0;
+
+	(void)arg;
+
+	while(file_info_.is_playing) {
+		if(!file_info_.eof) {
+			while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) {
+				unsigned s;
+
+				s = sample_buffer_last_ - sample_buffer_first_;
+				if(decoder_func_table_ -> is_eof(decoder_)) {
+					file_info_.eof = true;
+					break;
+				}
+				else if (!decoder_func_table_ -> process_single(decoder_)) {
+					/*@@@ this should probably be a dialog */
+					fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n");
+					file_info_.eof = true;
+					break;
+				}
+				blocksize = sample_buffer_last_ - sample_buffer_first_ - s;
+				decode_position_frame_last = decode_position_frame;
+				if(!decoder_func_table_->seekable || !FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame))
+					decode_position_frame = 0;
+			}
+			if(sample_buffer_last_ - sample_buffer_first_ > 0) {
+				const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE);
+				int bytes = n * file_info_.channels * file_info_.sample_format_bytes_per_sample;
+				FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * file_info_.channels * file_info_.sample_format_bytes_per_sample;
+				unsigned written_time, bh_index_w;
+				FLAC__uint64 decode_position;
+
+				sample_buffer_first_ += n;
+				while(flac_ip.output->buffer_free() < (int)bytes && file_info_.is_playing && file_info_.seek_to_in_sec == -1)
+					xmms_usleep(10000);
+				if(file_info_.is_playing && file_info_.seek_to_in_sec == -1)
+					produce_audio(flac_ip.output->written_time(), file_info_.sample_format,
+						file_info_.channels, bytes, sample_buffer_start, NULL);
+
+				/* compute current bitrate */
+
+				written_time = flac_ip.output->written_time();
+				bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE;
+				if(bh_index_w != bh_index_last_w) {
+					bh_index_last_w = bh_index_w;
+					decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize;
+					bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] =
+						decode_position > decode_position_last && written_time > written_time_last ?
+							8000 * (decode_position - decode_position_last) / (written_time - written_time_last) :
+							file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample;
+					decode_position_last = decode_position;
+					written_time_last = written_time;
+				}
+			}
+			else {
+				file_info_.eof = true;
+				xmms_usleep(10000);
+			}
+		}
+		else
+			xmms_usleep(10000);
+		if(decoder_func_table_->seekable && file_info_.seek_to_in_sec != -1) {
+			const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec;
+			unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples);
+			if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) {
+				flac_ip.output->flush(file_info_.seek_to_in_sec * 1000);
+				bh_index_last_w = bh_index_last_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE;
+				if(!FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame))
+					decode_position_frame = 0;
+				file_info_.seek_to_in_sec = -1;
+				file_info_.eof = false;
+				sample_buffer_first_ = sample_buffer_last_ = 0;
+			}
+		}
+		else {
+			/* display the right bitrate from history */
+			unsigned bh_index_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE;
+			if(bh_index_o != bh_index_last_o && bh_index_o != bh_index_last_w && bh_index_o != (bh_index_last_w + 1) % BITRATE_HIST_SIZE) {
+				bh_index_last_o = bh_index_o;
+				flac_ip.set_info(file_info_.title, file_info_.length_in_msec, bitrate_history_[bh_index_o], file_info_.sample_rate, file_info_.channels);
+			}
+		}
+	}
+
+	decoder_func_table_ -> safe_decoder_finish(decoder_);
+
+	/* are these two calls necessary? */
+	flac_ip.output->buffer_free();
+	flac_ip.output->buffer_free();
+
+	g_free(file_info_.title);
+
+	g_thread_exit(NULL);
+	return 0; /* to silence the compiler warning about not returning a value */
+}
+
+/*********** File decoder functions */
+
+static FLAC__bool file_decoder_init (void *decoder)
+{
+	return FLAC__file_decoder_init( (FLAC__FileDecoder*) decoder) == FLAC__FILE_DECODER_OK;
+}
+
+static void file_decoder_safe_decoder_finish_(void *decoder)
+{
+	if(decoder && FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
+		FLAC__file_decoder_finish((FLAC__FileDecoder *) decoder);
+}
+
+static void file_decoder_safe_decoder_delete_(void *decoder)
+{
+	if(decoder) {
+		file_decoder_safe_decoder_finish_(decoder);
+		FLAC__file_decoder_delete( (FLAC__FileDecoder *) decoder);
+	}
+}
+
+static FLAC__bool file_decoder_is_eof(void *decoder)
+{
+	return FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) == FLAC__FILE_DECODER_END_OF_FILE;
+}
+
+static const decoder_funcs_t FILE_DECODER_FUNCTIONS = {
+	true,
+	(void* (*) (void)) FLAC__file_decoder_new,
+	(FLAC__bool (*) (void *, FLAC__bool)) FLAC__file_decoder_set_md5_checking,
+	(FLAC__bool (*) (void *, const char*)) FLAC__file_decoder_set_filename,
+	(FLAC__bool (*) (void *)) FLAC__file_decoder_set_metadata_ignore_all,
+	(FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__file_decoder_set_metadata_respond,
+	(FLAC__bool (*) (void *, WriteCallback)) FLAC__file_decoder_set_write_callback,
+	(FLAC__bool (*) (void *, MetadataCallback)) FLAC__file_decoder_set_metadata_callback,
+	(FLAC__bool (*) (void *, ErrorCallback)) FLAC__file_decoder_set_error_callback,
+	(FLAC__bool (*) (void *, void *)) FLAC__file_decoder_set_client_data,
+	(FLAC__bool (*) (void *)) file_decoder_init,
+	(void (*) (void *)) file_decoder_safe_decoder_finish_,
+	(void (*) (void *)) file_decoder_safe_decoder_delete_,
+	(FLAC__bool (*) (void *)) FLAC__file_decoder_process_until_end_of_metadata,
+	(FLAC__bool (*) (void *)) FLAC__file_decoder_process_single,
+	file_decoder_is_eof
+};
+
+/*********** HTTP decoder functions */
+
+static gchar *url_;
+
+static FLAC__bool http_decoder_set_md5_checking (void *decoder, FLAC__bool value)
+{
+	(void) value;
+	// operation unsupported
+	return FLAC__stream_decoder_get_state ((const FLAC__StreamDecoder *) decoder) ==
+		FLAC__STREAM_DECODER_UNINITIALIZED;
+}
+
+static FLAC__bool http_decoder_set_url (void *decoder, const char* url)
+{
+	(void) decoder;
+	url_ = g_strdup (url);
+	return true;
+}
+
+static FLAC__StreamDecoderReadStatus http_decoder_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+	(void) decoder;
+	(void) client_data;
+	*bytes = flac_http_read (buffer, *bytes);
+	return *bytes ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+}
+
+static FLAC__bool http_decoder_init (void *decoder)
+{
+	flac_http_open (url_, 0);
+	g_free (url_);
+	FLAC__stream_decoder_set_read_callback (decoder, http_decoder_read_callback);
+	return FLAC__stream_decoder_init( (FLAC__StreamDecoder*) decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA;
+}
+
+static void http_decoder_safe_decoder_finish_(void *decoder)
+{
+	if(decoder && FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) {
+		FLAC__stream_decoder_finish((FLAC__StreamDecoder *) decoder);
+		flac_http_close();
+	}
+}
+
+static void http_decoder_safe_decoder_delete_(void *decoder)
+{
+	if(decoder) {
+		http_decoder_safe_decoder_finish_(decoder);
+		FLAC__stream_decoder_delete( (FLAC__StreamDecoder *) decoder);
+	}
+}
+
+static FLAC__bool http_decoder_is_eof(void *decoder)
+{
+	return FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) == FLAC__STREAM_DECODER_END_OF_STREAM;
+}
+
+static const decoder_funcs_t HTTP_DECODER_FUNCTIONS = {
+	false,
+	(void* (*) (void)) FLAC__stream_decoder_new,
+	http_decoder_set_md5_checking,
+	(FLAC__bool (*) (void *, const char*)) http_decoder_set_url,
+	(FLAC__bool (*) (void *)) FLAC__stream_decoder_set_metadata_ignore_all,
+	(FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__stream_decoder_set_metadata_respond,
+	(FLAC__bool (*) (void *, WriteCallback)) FLAC__stream_decoder_set_write_callback,
+	(FLAC__bool (*) (void *, MetadataCallback)) FLAC__stream_decoder_set_metadata_callback,
+	(FLAC__bool (*) (void *, ErrorCallback)) FLAC__stream_decoder_set_error_callback,
+	(FLAC__bool (*) (void *, void *)) FLAC__stream_decoder_set_client_data,
+	(FLAC__bool (*) (void *)) http_decoder_init,
+	(void (*) (void *)) http_decoder_safe_decoder_finish_,
+	(void (*) (void *)) http_decoder_safe_decoder_delete_,
+	(FLAC__bool (*) (void *)) FLAC__stream_decoder_process_until_end_of_metadata,
+	(FLAC__bool (*) (void *)) FLAC__stream_decoder_process_single,
+	http_decoder_is_eof
+};
+
+static decoder_funcs_t const *decoder_func_table_;
+
+static void init_decoder_func_tables()
+{
+	DECODER_FUNCS [DECODER_FILE] = & FILE_DECODER_FUNCTIONS;
+	DECODER_FUNCS [DECODER_HTTP] = & HTTP_DECODER_FUNCTIONS;
+}
+
+static decoder_t source_to_decoder_type (const char *source)
+{
+	return strncasecmp(source, "http://", 7) ? DECODER_FILE : DECODER_HTTP;
+}
+
+static void change_decoder_if_needed (decoder_t new_decoder_type, void **decoderp, decoder_funcs_t const ** fntabp)
+{
+	const decoder_funcs_t *new_fn_table = DECODER_FUNCS [new_decoder_type];
+	if (*fntabp != new_fn_table) {
+		(*fntabp)->safe_decoder_delete(*decoderp);
+		*fntabp = new_fn_table;
+		*decoderp = new_fn_table -> new_decoder();
+	}
+}
+
+FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fntabp)
+{
+	if(decoderp == 0 || *decoderp == 0)
+		return false;
+
+	(*fntabp)->safe_decoder_finish(*decoderp);
+
+	change_decoder_if_needed(source_to_decoder_type(filename), decoderp, fntabp);
+
+	{
+		decoder_funcs_t const *fntab = *fntabp;
+		void *decoder = *decoderp;
+
+		decoder = *decoderp;
+		fntab = *fntabp;
+
+		fntab -> set_md5_checking(decoder, false);
+		fntab -> set_source(decoder, filename);
+		fntab -> set_metadata_ignore_all(decoder);
+		fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
+		fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+		fntab -> set_write_callback(decoder, write_callback_);
+		fntab -> set_metadata_callback(decoder, metadata_callback_);
+		fntab -> set_error_callback(decoder, error_callback_);
+		fntab -> set_client_data(decoder, &file_info_);
+		if(!fntab -> decoder_init(decoder))
+			return false;
+
+		if(!fntab -> process_until_end_of_metadata(decoder))
+			return false;
+	}
+
+	return true;
+}
+
+FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+	file_info_struct *file_info = (file_info_struct *)client_data;
+	const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize;
+	const unsigned bits_per_sample = file_info->bits_per_sample;
+	FLAC__byte *sample_buffer_start;
+
+	(void)decoder;
+
+	if(file_info->abort_flag)
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+	if((sample_buffer_last_ + wide_samples) > (SAMPLE_BUFFER_SIZE / (channels * file_info->sample_format_bytes_per_sample))) {
+		memmove(sample_buffer_, sample_buffer_ + sample_buffer_first_ * channels * file_info->sample_format_bytes_per_sample, (sample_buffer_last_ - sample_buffer_first_) * channels * file_info->sample_format_bytes_per_sample);
+		sample_buffer_last_ -= sample_buffer_first_;
+		sample_buffer_first_ = 0;
+	}
+	sample_buffer_start = sample_buffer_ + sample_buffer_last_ * channels * file_info->sample_format_bytes_per_sample;
+	if(file_info->has_replaygain && flac_cfg.output.replaygain.enable) {
+		FLAC__replaygain_synthesis__apply_gain(
+				sample_buffer_start,
+				!is_big_endian_host_,
+				file_info->sample_format_bytes_per_sample == 1, /* unsigned_data_out */
+				buffer,
+				wide_samples,
+				channels,
+				bits_per_sample,
+				file_info->sample_format_bytes_per_sample * 8,
+				file_info->replay_scale,
+				flac_cfg.output.replaygain.hard_limit,
+				flac_cfg.output.resolution.replaygain.dither,
+				&file_info->dither_context
+		);
+	}
+	else if(is_big_endian_host_) {
+		FLAC__plugin_common__pack_pcm_signed_big_endian(
+			sample_buffer_start,
+			buffer,
+			wide_samples,
+			channels,
+			bits_per_sample,
+			file_info->sample_format_bytes_per_sample * 8
+		);
+	}
+	else {
+		FLAC__plugin_common__pack_pcm_signed_little_endian(
+			sample_buffer_start,
+			buffer,
+			wide_samples,
+			channels,
+			bits_per_sample,
+			file_info->sample_format_bytes_per_sample * 8
+		);
+	}
+
+	sample_buffer_last_ += wide_samples;
+
+	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+	file_info_struct *file_info = (file_info_struct *)client_data;
+	(void)decoder;
+	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+		FLAC__ASSERT(metadata->data.stream_info.total_samples < FLAC__U64L(0x100000000)); /* this plugin can only handle < 4 gigasamples */
+		file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
+		file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;
+		file_info->channels = metadata->data.stream_info.channels;
+		file_info->sample_rate = metadata->data.stream_info.sample_rate;
+		file_info->length_in_msec = (unsigned)((double)file_info->total_samples / (double)file_info->sample_rate * 1000.0 + 0.5);
+	}
+	else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+		double gain, peak;
+		if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) {
+			file_info->has_replaygain = true;
+			file_info->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit);
+		}
+	}
+}
+
+void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+	file_info_struct *file_info = (file_info_struct *)client_data;
+	(void)decoder;
+	if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
+		file_info->abort_flag = true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,24 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2004,2005  Josh Coalson
+ *
+ * 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 FLAC__PLUGIN_XMMS__PLUGIN_H
+#define FLAC__PLUGIN_XMMS__PLUGIN_H
+
+void set_track_info(const char* title, int length_in_msec);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/Makefile	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,26 @@
+include ../../../mk/rules.mk
+include ../../../mk/init.mk
+
+CFLAGS += $(PICFLAGS) -I.. -I../../..
+
+OBJECTIVE_LIBS_NOINST = libplugin_common.a
+
+noinst_HEADERS = \
+	all.h \
+	charset.h \
+	defs.h \
+	dither.h \
+	locale_hack.h \
+	tags.h
+
+SOURCES = \
+	charset.c \
+	dither.c \
+	tags.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+libplugin_common.a: $(OBJECTS)
+	$(AR) cq $@ $(OBJECTS)
+
+include ../../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/Makefile.lite	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,35 @@
+#  plugin_common - Routines common to several plugins
+#  Copyright (C) 2002,2003,2004,2005  Josh Coalson
+#
+#  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.
+
+#
+# GNU makefile
+#
+
+topdir = ../..
+
+LIB_NAME = libplugin_common
+INCLUDES = -I$(topdir)/include -I$(HOME)/local/include -I$(ICONV_INCLUDE_DIR)
+DEFINES  = 
+
+SRCS_C = \
+	charset.c \
+	dither.c \
+	tags.c
+
+include $(topdir)/build/lib.mk
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/README	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,2 @@
+This directory contains a convenience library of routines that are
+common to the plugins.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/all.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,27 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 FLAC__PLUGIN_COMMON__ALL_H
+#define FLAC__PLUGIN_COMMON__ALL_H
+
+#include "charset.h"
+#include "dither.h"
+#include "locale_hack.h"
+#include "tags.h"
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/charset.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,156 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * Only slightly modified charset.c from:
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 1999-2001  Håvard Kvålen <havardk@xmms.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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "charset.h"
+
+
+/*************
+ * Functions *
+ *************/
+
+char* FLAC_plugin__charset_get_current (void)
+{
+	char *charset = getenv("CHARSET");
+
+#ifdef HAVE_LANGINFO_CODESET
+	if (!charset)
+		charset = nl_langinfo(CODESET);
+#endif
+
+    if (charset)
+        return strdup(charset);
+
+    return strdup("ISO-8859-1");
+}
+
+
+#ifdef HAVE_ICONV
+char* FLAC_plugin__charset_convert_string (const char *string, char *from, char *to)
+{
+	size_t outleft, outsize, length;
+	iconv_t cd;
+	char *out, *outptr;
+	const char *input = string;
+
+	if (!string)
+		return NULL;
+
+	length = strlen(string);
+
+	if ((cd = iconv_open(to, from)) == (iconv_t)-1)
+	{
+#ifdef DEBUG
+		fprintf(stderr, "convert_string(): Conversion not supported. Charsets: %s -> %s", from, to);
+#endif
+		return strdup(string);
+	}
+
+	/* Due to a GLIBC bug, round outbuf_size up to a multiple of 4 */
+	/* + 1 for nul in case len == 1 */
+	outsize = ((length + 3) & ~3) + 1;
+	out = (char*)malloc(outsize);
+	outleft = outsize - 1;
+	outptr = out;
+
+retry:
+#if defined __OpenBSD__ || defined __NetBSD__
+	if (iconv(cd, &input, &length, &outptr, &outleft) == (size_t)-1)
+#else
+	if (iconv(cd, (char**)&input, &length, &outptr, &outleft) == (size_t)-1)
+#endif
+	{
+		int used;
+		switch (errno)
+		{
+			case E2BIG:
+				used = outptr - out;
+				outsize = (outsize - 1) * 2 + 1;
+				out = realloc(out, outsize);
+				outptr = out + used;
+				outleft = outsize - 1 - used;
+				goto retry;
+			case EINVAL:
+				break;
+			case EILSEQ:
+				/* Invalid sequence, try to get the rest of the string */
+				input++;
+				length = strlen(input);
+				goto retry;
+			default:
+#ifdef DEBUG
+				fprintf(stderr, "convert_string(): Conversion failed. Inputstring: %s; Error: %s", string, strerror(errno));
+#endif
+				break;
+		}
+	}
+	*outptr = '\0';
+
+	iconv_close(cd);
+	return out;
+}
+#else
+char* FLAC_plugin__charset_convert_string (const char *string, char *from, char *to)
+{
+	(void)from, (void)to;
+	if (!string)
+		return NULL;
+	return strdup(string);
+}
+#endif
+
+#ifdef HAVE_ICONV
+int FLAC_plugin__charset_test_conversion (char *from, char *to)
+{
+	iconv_t cd;
+
+	if ((cd=iconv_open(to,from)) == (iconv_t)-1)
+	{
+		/* Conversion not supported */
+		return 0;
+	}
+	iconv_close(cd);
+	return 1;
+}
+#else
+int FLAC_plugin__charset_test_conversion (char *from, char *to)
+{
+	(void)from, (void)to;
+	return 1;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/charset.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,39 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * Only slightly modified charset.h from:
+ * charset.h - 2001/12/04
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 1999-2001  H蛆ard Kv虱en <havardk@xmms.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 FLAC__PLUGIN_COMMON__CHARSET_H
+#define FLAC__PLUGIN_COMMON__CHARSET_H
+
+
+/**************
+ * Prototypes *
+ **************/
+
+char *FLAC_plugin__charset_get_current();
+char *FLAC_plugin__charset_convert_string(const char *string, char *from, char *to);
+
+/* returns 1 for success, 0 for failure or no iconv */
+int FLAC_plugin__charset_test_conversion(char *from, char *to);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/defs.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,24 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 FLAC__PLUGIN_COMMON__DEFS_H
+#define FLAC__PLUGIN_COMMON__DEFS_H
+
+#define FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS 2
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/dither.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,260 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * dithering routine derived from (other GPLed source):
+ * mad - MPEG audio decoder
+ * Copyright (C) 2000-2001 Robert Leslie
+ *
+ * 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 "dither.h"
+#include "FLAC/assert.h"
+
+#ifdef max
+#undef max
+#endif
+#define max(a,b) ((a)>(b)?(a):(b))
+
+
+#if defined _MSC_VER
+#define FLAC__INLINE __inline
+#else
+#define FLAC__INLINE
+#endif
+
+/* 32-bit pseudo-random number generator
+ *
+ * @@@ According to Miroslav, this one is poor quality, the one from the
+ * @@@ original replaygain code is much better
+ */
+static FLAC__INLINE FLAC__uint32 prng(FLAC__uint32 state)
+{
+	return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+}
+
+/* dither routine derived from MAD winamp plugin */
+
+typedef struct {
+	FLAC__int32 error[3];
+	FLAC__int32 random;
+} dither_state;
+
+static FLAC__INLINE FLAC__int32 linear_dither(unsigned source_bps, unsigned target_bps, FLAC__int32 sample, dither_state *dither, const FLAC__int32 MIN, const FLAC__int32 MAX)
+{
+	unsigned scalebits;
+	FLAC__int32 output, mask, random;
+
+	FLAC__ASSERT(source_bps < 32);
+	FLAC__ASSERT(target_bps <= 24);
+	FLAC__ASSERT(target_bps <= source_bps);
+
+	/* noise shape */
+	sample += dither->error[0] - dither->error[1] + dither->error[2];
+
+	dither->error[2] = dither->error[1];
+	dither->error[1] = dither->error[0] / 2;
+
+	/* bias */
+	output = sample + (1L << (source_bps - target_bps - 1));
+
+	scalebits = source_bps - target_bps;
+	mask = (1L << scalebits) - 1;
+
+	/* dither */
+	random = (FLAC__int32)prng(dither->random);
+	output += (random & mask) - (dither->random & mask);
+
+	dither->random = random;
+
+	/* clip */
+	if(output > MAX) {
+		output = MAX;
+
+		if(sample > MAX)
+			sample = MAX;
+	}
+	else if(output < MIN) {
+		output = MIN;
+
+		if(sample < MIN)
+			sample = MIN;
+	}
+
+	/* quantize */
+	output &= ~mask;
+
+	/* error feedback */
+	dither->error[0] = sample - output;
+
+	/* scale */
+	return output >> scalebits;
+}
+
+size_t FLAC__plugin_common__pack_pcm_signed_big_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps)
+{
+	static dither_state dither[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
+	FLAC__byte * const start = data;
+	FLAC__int32 sample;
+	const FLAC__int32 *input_;
+	unsigned samples, channel;
+	const unsigned bytes_per_sample = target_bps / 8;
+	const unsigned incr = bytes_per_sample * channels;
+
+	FLAC__ASSERT(channels > 0 && channels <= FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS);
+	FLAC__ASSERT(source_bps < 32);
+	FLAC__ASSERT(target_bps <= 24);
+	FLAC__ASSERT(target_bps <= source_bps);
+	FLAC__ASSERT((source_bps & 7) == 0);
+	FLAC__ASSERT((target_bps & 7) == 0);
+
+	if(source_bps != target_bps) {
+		const FLAC__int32 MIN = -(1L << (source_bps - 1));
+		const FLAC__int32 MAX = ~MIN; /*(1L << (source_bps-1)) - 1 */
+
+		for(channel = 0; channel < channels; channel++) {
+			
+			samples = wide_samples;
+			data = start + bytes_per_sample * channel;
+			input_ = input[channel];
+
+			while(samples--) {
+				sample = linear_dither(source_bps, target_bps, *input_++, &dither[channel], MIN, MAX);
+
+				switch(target_bps) {
+					case 8:
+						data[0] = sample ^ 0x80;
+						break;
+					case 16:
+						data[0] = (FLAC__byte)(sample >> 8);
+						data[1] = (FLAC__byte)sample;
+						break;
+					case 24:
+						data[0] = (FLAC__byte)(sample >> 16);
+						data[1] = (FLAC__byte)(sample >> 8);
+						data[2] = (FLAC__byte)sample;
+						break;
+				}
+
+				data += incr;
+			}
+		}
+	}
+	else {
+		for(channel = 0; channel < channels; channel++) {
+			samples = wide_samples;
+			data = start + bytes_per_sample * channel;
+			input_ = input[channel];
+
+			while(samples--) {
+				sample = *input_++;
+
+				switch(target_bps) {
+					case 8:
+						data[0] = sample ^ 0x80;
+						break;
+					case 16:
+						data[0] = (FLAC__byte)(sample >> 8);
+						data[1] = (FLAC__byte)sample;
+						break;
+					case 24:
+						data[0] = (FLAC__byte)(sample >> 16);
+						data[1] = (FLAC__byte)(sample >> 8);
+						data[2] = (FLAC__byte)sample;
+						break;
+				}
+
+				data += incr;
+			}
+		}
+	}
+
+	return wide_samples * channels * (target_bps/8);
+}
+
+size_t FLAC__plugin_common__pack_pcm_signed_little_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps)
+{
+	static dither_state dither[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
+	FLAC__byte * const start = data;
+	FLAC__int32 sample;
+	const FLAC__int32 *input_;
+	unsigned samples, channel;
+	const unsigned bytes_per_sample = target_bps / 8;
+	const unsigned incr = bytes_per_sample * channels;
+
+	FLAC__ASSERT(channels > 0 && channels <= FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS);
+	FLAC__ASSERT(source_bps < 32);
+	FLAC__ASSERT(target_bps <= 24);
+	FLAC__ASSERT(target_bps <= source_bps);
+	FLAC__ASSERT((source_bps & 7) == 0);
+	FLAC__ASSERT((target_bps & 7) == 0);
+
+	if(source_bps != target_bps) {
+		const FLAC__int32 MIN = -(1L << (source_bps - 1));
+		const FLAC__int32 MAX = ~MIN; /*(1L << (source_bps-1)) - 1 */
+
+		for(channel = 0; channel < channels; channel++) {
+			
+			samples = wide_samples;
+			data = start + bytes_per_sample * channel;
+			input_ = input[channel];
+
+			while(samples--) {
+				sample = linear_dither(source_bps, target_bps, *input_++, &dither[channel], MIN, MAX);
+
+				switch(target_bps) {
+					case 8:
+						data[0] = sample ^ 0x80;
+						break;
+					case 24:
+						data[2] = (FLAC__byte)(sample >> 16);
+						/* fall through */
+					case 16:
+						data[1] = (FLAC__byte)(sample >> 8);
+						data[0] = (FLAC__byte)sample;
+				}
+
+				data += incr;
+			}
+		}
+	}
+	else {
+		for(channel = 0; channel < channels; channel++) {
+			samples = wide_samples;
+			data = start + bytes_per_sample * channel;
+			input_ = input[channel];
+
+			while(samples--) {
+				sample = *input_++;
+
+				switch(target_bps) {
+					case 8:
+						data[0] = sample ^ 0x80;
+						break;
+					case 24:
+						data[2] = (FLAC__byte)(sample >> 16);
+						/* fall through */
+					case 16:
+						data[1] = (FLAC__byte)(sample >> 8);
+						data[0] = (FLAC__byte)sample;
+				}
+
+				data += incr;
+			}
+		}
+	}
+
+	return wide_samples * channels * (target_bps/8);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/dither.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,29 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 FLAC__PLUGIN_COMMON__DITHER_H
+#define FLAC__PLUGIN_COMMON__DITHER_H
+
+#include <stdlib.h> /* for size_t */
+#include "defs.h" /* buy FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS for the caller */
+#include "FLAC/ordinals.h"
+
+size_t FLAC__plugin_common__pack_pcm_signed_big_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps);
+size_t FLAC__plugin_common__pack_pcm_signed_little_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/locale_hack.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,55 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * Based on:
+ * locale.h - 2000/05/05 13:10 Jerome Couderc
+ *  EasyTAG - Tag editor for MP3 and OGG files
+ *  Copyright (C) 1999-2001  H蛆ard Kv虱en <havardk@xmms.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.
+ */
+/*
+ * Gettext support for EasyTAG
+ */
+
+
+#ifndef FLAC__PLUGIN_COMMON__LOCALE_HACK_H
+#define FLAC__PLUGIN_COMMON__LOCALE_HACK_H
+
+#include <locale.h>
+
+/*
+ * Standard gettext macros.
+ */
+#ifdef ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) gettext (String)
+#  ifdef gettext_noop
+#    define N_(String) gettext_noop (String)
+#  else
+#    define N_(String) (String)
+#  endif
+#else
+#  define textdomain(String) (String)
+#  define gettext(String) (String)
+#  define dgettext(Domain,Message) (Message)
+#  define dcgettext(Domain,Message,Type) (Message)
+#  define bindtextdomain(Domain,Directory) (Domain)
+#  define _(String) (String)
+#  define N_(String) (String)
+#endif
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/plugin_common_static.dsp	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,124 @@
+# Microsoft Developer Studio Project File - Name="plugin_common_static" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=plugin_common_static - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "plugin_common_static.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "plugin_common_static.mak" CFG="plugin_common_static - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "plugin_common_static - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "plugin_common_static - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "plugin_common"
+# PROP Scc_LocalPath "..\.."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "plugin_common_static - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\obj\release\lib"
+# PROP Intermediate_Dir "Release_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /WX /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /nodefaultlib
+
+!ELSEIF  "$(CFG)" == "plugin_common_static - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\obj\debug\lib"
+# PROP Intermediate_Dir "Debug_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /nodefaultlib
+
+!ENDIF 
+
+# Begin Target
+
+# Name "plugin_common_static - Win32 Release"
+# Name "plugin_common_static - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp"
+# Begin Source File
+
+SOURCE=.\charset.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dither.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tags.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\all.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\charset.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dither.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\locale_hack.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\tags.h
+# End Source File
+# End Group
+# End Target
+# End Project
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/tags.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,307 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "tags.h"
+#include "FLAC/assert.h"
+#include "FLAC/metadata.h"
+
+
+static __inline unsigned local__wide_strlen(const FLAC__uint16 *s)
+{
+	unsigned n = 0;
+	while(*s++)
+		n++;
+	return n;
+}
+
+static __inline unsigned local__utf8len(const FLAC__byte *utf8)
+{
+	FLAC__ASSERT(0 != utf8);
+	if ((utf8[0] & 0x80) == 0)
+		return 1;
+	else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80)
+		return 2;
+	else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80)
+		return 3;
+	else
+		return 0;
+}
+
+static __inline unsigned local__utf8_to_ucs2(const FLAC__byte *utf8, FLAC__uint16 *ucs2)
+{
+	const unsigned len = local__utf8len(utf8);
+
+	FLAC__ASSERT(0 != ucs2);
+
+	if (len == 1)
+		*ucs2 = *utf8;
+	else if (len == 2)
+		*ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F);
+	else if (len == 3)
+		*ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F);
+
+	return len;
+}
+
+static FLAC__uint16 *local__convert_utf8_to_ucs2(const char *src, unsigned length)
+{
+	FLAC__uint16 *out;
+	unsigned chars = 0;
+
+	FLAC__ASSERT(0 != src);
+
+	/* calculate length */
+	{
+		const char *s, *end;
+		for (s=src, end=src+length; s<end; chars++) {
+			const unsigned n = local__utf8len((unsigned char*)s);
+			if (n == 0)
+				return 0;
+			s += n;
+		}
+		FLAC__ASSERT(s == end);
+	}
+
+	/* allocate */
+	out = (FLAC__uint16*)malloc(chars * sizeof(FLAC__uint16));
+	if (0 == out) {
+		FLAC__ASSERT(0);
+		return 0;
+	}
+
+	/* convert */
+	{
+		FLAC__uint16 *u = out;
+		for ( ; chars; chars--)
+			src += local__utf8_to_ucs2((const unsigned char*)src, u++);
+	}
+
+	return out;
+}
+
+static __inline unsigned local__ucs2len(FLAC__uint16 ucs2)
+{
+	if (ucs2 < 0x0080)
+		return 1;
+	else if (ucs2 < 0x0800)
+		return 2;
+	else
+		return 3;
+}
+
+static __inline unsigned local__ucs2_to_utf8(FLAC__uint16 ucs2, FLAC__byte *utf8)
+{
+	if (ucs2 < 0x080) {
+		utf8[0] = (FLAC__byte)ucs2;
+		return 1;
+	}
+	else if (ucs2 < 0x800) {
+		utf8[0] = 0xc0 | (ucs2 >> 6);
+		utf8[1] = 0x80 | (ucs2 & 0x3f);
+		return 2;
+	}
+	else {
+		utf8[0] = 0xe0 | (ucs2 >> 12);
+		utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f);
+		utf8[2] = 0x80 | (ucs2 & 0x3f);
+		return 3;
+	}
+}
+
+static char *local__convert_ucs2_to_utf8(const FLAC__uint16 *src, unsigned length)
+{
+	char *out;
+	unsigned len = 0;
+
+	FLAC__ASSERT(0 != src);
+
+	/* calculate length */
+	{
+		unsigned i;
+		for (i = 0; i < length; i++)
+			len += local__ucs2len(src[i]);
+	}
+
+	/* allocate */
+	out = (char*)malloc(len * sizeof(char));
+	if (0 == out)
+		return 0;
+
+	/* convert */
+	{
+		unsigned char *u = (unsigned char*) out;
+		for ( ; *src; src++)
+			u += local__ucs2_to_utf8(*src, u);
+		local__ucs2_to_utf8(*src, u);
+	}
+
+	return out;
+}
+
+
+FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags)
+{
+	if(!FLAC__metadata_get_tags(filename, tags))
+		if(0 == (*tags = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)))
+			return false;
+	return true;
+}
+
+FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags)
+{
+	FLAC__Metadata_Chain *chain;
+	FLAC__Metadata_Iterator *iterator;
+	FLAC__StreamMetadata *block;
+	FLAC__bool got_vorbis_comments = false;
+	FLAC__bool ok;
+
+	if(0 == (chain = FLAC__metadata_chain_new()))
+		return false;
+
+	if(!FLAC__metadata_chain_read(chain, filename)) {
+		FLAC__metadata_chain_delete(chain);
+		return false;
+	}
+
+	if(0 == (iterator = FLAC__metadata_iterator_new())) {
+		FLAC__metadata_chain_delete(chain);
+		return false;
+	}
+
+	FLAC__metadata_iterator_init(iterator, chain);
+
+	do {
+		if(FLAC__metadata_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+			got_vorbis_comments = true;
+	} while(!got_vorbis_comments && FLAC__metadata_iterator_next(iterator));
+
+	if(0 == (block = FLAC__metadata_object_clone(tags))) {
+		FLAC__metadata_chain_delete(chain);
+		FLAC__metadata_iterator_delete(iterator);
+		return false;
+	}
+
+	if(got_vorbis_comments)
+		ok = FLAC__metadata_iterator_set_block(iterator, block);
+	else
+		ok = FLAC__metadata_iterator_insert_block_after(iterator, block);
+
+	FLAC__metadata_iterator_delete(iterator);
+
+	if(ok) {
+		FLAC__metadata_chain_sort_padding(chain);
+		ok = FLAC__metadata_chain_write(chain, /*use_padding=*/true, /*preserve_file_stats=*/true);
+	}
+
+	FLAC__metadata_chain_delete(chain);
+
+	return ok;
+}
+
+void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags)
+{
+	FLAC__metadata_object_delete(*tags);
+	*tags = 0;
+}
+
+const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name)
+{
+	const int i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name);
+	return (i < 0? 0 : strchr((const char*)tags->data.vorbis_comment.comments[i].entry, '=')+1);
+}
+
+FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name)
+{
+	const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name);
+	if(0 == utf8)
+		return 0;
+	return local__convert_utf8_to_ucs2(utf8, strlen(utf8)+1); /* +1 for terminating null */
+}
+
+int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name)
+{
+	return FLAC__metadata_object_vorbiscomment_remove_entries_matching(tags, name);
+}
+
+int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags)
+{
+	int n = (int)tags->data.vorbis_comment.num_comments;
+	if(n > 0) {
+		if(!FLAC__metadata_object_vorbiscomment_resize_comments(tags, 0))
+			n = -1;
+	}
+	return n;
+}
+
+FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator)
+{
+	int i;
+
+	FLAC__ASSERT(0 != tags);
+	FLAC__ASSERT(0 != name);
+	FLAC__ASSERT(0 != value);
+
+	if(separator && (i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name)) >= 0) {
+		FLAC__StreamMetadata_VorbisComment_Entry *entry = tags->data.vorbis_comment.comments+i;
+		const size_t value_len = strlen(value);
+		const size_t separator_len = strlen(separator);
+		FLAC__byte *new_entry;
+		if(0 == (new_entry = (FLAC__byte*)realloc(entry->entry, entry->length + value_len + separator_len + 1)))
+			return false;
+		memcpy(new_entry+entry->length, separator, separator_len);
+		entry->length += separator_len;
+		memcpy(new_entry+entry->length, value, value_len);
+		entry->length += value_len;
+		new_entry[entry->length] = '\0';
+		entry->entry = new_entry;
+	}
+	else {
+		FLAC__StreamMetadata_VorbisComment_Entry entry;
+		if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, value))
+			return false;
+		FLAC__metadata_object_vorbiscomment_append_comment(tags, entry, /*copy=*/false);
+	}
+	return true;
+}
+
+FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all)
+{
+	FLAC__StreamMetadata_VorbisComment_Entry entry;
+
+	FLAC__ASSERT(0 != tags);
+	FLAC__ASSERT(0 != name);
+	FLAC__ASSERT(0 != value);
+
+	{
+		char *utf8 = local__convert_ucs2_to_utf8(value, local__wide_strlen(value)+1); /* +1 for the terminating null */
+		if(0 == utf8)
+			return false;
+		if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, utf8)) {
+			free(utf8);
+			return false;
+		}
+		free(utf8);
+	}
+	if(!FLAC__metadata_object_vorbiscomment_replace_comment(tags, entry, replace_all, /*copy=*/false))
+		return false;
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/plugin_common/tags.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,74 @@
+/* plugin_common - Routines common to several plugins
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 FLAC__PLUGIN_COMMON__TAGS_H
+#define FLAC__PLUGIN_COMMON__TAGS_H
+
+#include "FLAC/format.h"
+
+FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags);
+FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags);
+
+/*
+ * Deletes the tags object and sets '*tags' to NULL.
+ */
+void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags);
+
+/*
+ * Gets the value (in UTF-8) of the first tag with the given name (NULL if no
+ * such tag exists).
+ */
+const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name);
+
+/*
+ * Gets the value (in UCS-2) of the first tag with the given name (NULL if no
+ * such tag exists).
+ *
+ * NOTE: the returned string is malloc()ed and must be free()d by the caller.
+ */
+FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name);
+
+/*
+ * Removes all tags with the given 'name'.  Returns the number of tags removed,
+ * or -1 on memory allocation error.
+ */
+int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name);
+
+/*
+ * Removes all tags.  Returns the number of tags removed, or -1 on memory
+ * allocation error.
+ */
+int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags);
+
+/*
+ * Adds a "name=value" tag to the tags.  'value' must be in UTF-8.  If
+ * 'separator' is non-NULL and 'tags' already contains a tag for 'name', the
+ * first such tag's value is appended with separator, then value.
+ */
+FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator);
+
+/*
+ * Adds a "name=value" tag to the tags.  'value' must be in UCS-2.  If 'tags'
+ * already contains a tag or tags for 'name', then they will be replaced
+ * according to 'replace_all': if 'replace_all' is false, only the first such
+ * tag will be replaced; if true, all matching tags will be replaced by the one
+ * new tag. 
+ */
+FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/replaygain.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,615 @@
+/* grabbag - Convenience lib for various routines common to several tools
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 "grabbag.h"
+#include "replaygain_analysis.h"
+#include "FLAC/assert.h"
+#include "FLAC/file_decoder.h"
+#include "FLAC/metadata.h"
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined _MSC_VER || defined __MINGW32__
+#include <io.h> /* for chmod() */
+#endif
+#include <sys/stat.h> /* for stat(), maybe chmod() */
+
+#ifdef local_min
+#undef local_min
+#endif
+#define local_min(a,b) ((a)<(b)?(a):(b))
+
+#ifdef local_max
+#undef local_max
+#endif
+#define local_max(a,b) ((a)>(b)?(a):(b))
+
+static const FLAC__byte *tag_title_gain_ = (FLAC__byte*)"REPLAYGAIN_TRACK_GAIN";
+static const FLAC__byte *tag_title_peak_ = (FLAC__byte*)"REPLAYGAIN_TRACK_PEAK";
+static const FLAC__byte *tag_album_gain_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_GAIN";
+static const FLAC__byte *tag_album_peak_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_PEAK";
+static const char *peak_format_ = "%s=%1.8f";
+static const char *gain_format_ = "%s=%+2.2f dB";
+
+static double album_peak_, title_peak_;
+
+const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 148;
+/*
+	FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 +
+	FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 +
+	FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 +
+	FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12
+*/
+
+
+static FLAC__bool get_file_stats_(const char *filename, struct stat *stats)
+{
+	FLAC__ASSERT(0 != filename);
+	FLAC__ASSERT(0 != stats);
+	return (0 == stat(filename, stats));
+}
+
+static void set_file_stats_(const char *filename, struct stat *stats)
+{
+	FLAC__ASSERT(0 != filename);
+	FLAC__ASSERT(0 != stats);
+
+	(void)chmod(filename, stats->st_mode);
+}
+
+static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, const FLAC__byte *name, float value)
+{
+	char buffer[256];
+	char *saved_locale;
+	FLAC__StreamMetadata_VorbisComment_Entry entry;
+
+	FLAC__ASSERT(0 != block);
+	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+	FLAC__ASSERT(0 != name);
+	FLAC__ASSERT(0 != value);
+
+	buffer[sizeof(buffer)-1] = '\0';
+	/*
+	 * We need to save the old locale and switch to "C" because the locale
+	 * influences the formatting of %f and we want it a certain way.
+	 */
+	saved_locale = setlocale(LC_ALL, 0);
+	setlocale(LC_ALL, "C");
+#if defined _MSC_VER || defined __MINGW32__
+	_snprintf(buffer, sizeof(buffer)-1, format, name, value);
+#else
+	snprintf(buffer, sizeof(buffer)-1, format, name, value);
+#endif
+	setlocale(LC_ALL, saved_locale);
+
+	entry.entry = (FLAC__byte *)buffer;
+	entry.length = strlen(buffer);
+
+	return FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true);
+}
+
+FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency)
+{
+	static const unsigned valid_sample_rates[] = {
+		8000,
+		11025,
+		12000,
+		16000,
+		22050,
+		24000,
+		32000,
+		44100,
+		48000
+	};
+	static const unsigned n_valid_sample_rates = sizeof(valid_sample_rates) / sizeof(valid_sample_rates[0]);
+
+	unsigned i;
+
+	for(i = 0; i < n_valid_sample_rates; i++)
+		if(sample_frequency == valid_sample_rates[i])
+			return true;
+	return false;
+}
+
+FLAC__bool grabbag__replaygain_init(unsigned sample_frequency)
+{
+	title_peak_ = album_peak_ = 0.0;
+	return InitGainAnalysis((long)sample_frequency) == INIT_GAIN_ANALYSIS_OK;
+}
+
+FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples)
+{
+	/* using a small buffer improves data locality; we'd like it to fit easily in the dcache */
+	static Float_t lbuffer[2048], rbuffer[2048];
+	static const unsigned nbuffer = sizeof(lbuffer) / sizeof(lbuffer[0]);
+	FLAC__int32 block_peak = 0, s;
+	unsigned i, j;
+
+	FLAC__ASSERT(bps >= 4 && bps <= FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE);
+	FLAC__ASSERT(FLAC__MIN_BITS_PER_SAMPLE == 4);
+	/*
+	 * We use abs() on a FLAC__int32 which is undefined for the most negative value.
+	 * If the reference codec ever handles 32bps we will have to write a special
+	 * case here.
+	 */
+	FLAC__ASSERT(FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE < 32);
+
+	if(bps == 16) {
+		if(is_stereo) {
+			j = 0;
+			while(samples > 0) {
+				const unsigned n = local_min(samples, nbuffer);
+				for(i = 0; i < n; i++, j++) {
+					s = input[0][j];
+					lbuffer[i] = (Float_t)s;
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+
+					s = input[1][j];
+					rbuffer[i] = (Float_t)s;
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+				}
+				samples -= n;
+				if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
+					return false;
+			}
+		}
+		else {
+			j = 0;
+			while(samples > 0) {
+				const unsigned n = local_min(samples, nbuffer);
+				for(i = 0; i < n; i++, j++) {
+					s = input[0][j];
+					lbuffer[i] = (Float_t)s;
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+				}
+				samples -= n;
+				if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
+					return false;
+			}
+		}
+	}
+	else { /* bps must be < 32 according to above assertion */
+		const double scale = (
+			(bps > 16)?
+				(double)1. / (double)(1u << (bps - 16)) :
+				(double)(1u << (16 - bps))
+		);
+
+		if(is_stereo) {
+			j = 0;
+			while(samples > 0) {
+				const unsigned n = local_min(samples, nbuffer);
+				for(i = 0; i < n; i++, j++) {
+					s = input[0][j];
+					lbuffer[i] = (Float_t)(scale * (double)s);
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+
+					s = input[1][j];
+					rbuffer[i] = (Float_t)(scale * (double)s);
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+				}
+				samples -= n;
+				if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
+					return false;
+			}
+		}
+		else {
+			j = 0;
+			while(samples > 0) {
+				const unsigned n = local_min(samples, nbuffer);
+				for(i = 0; i < n; i++, j++) {
+					s = input[0][j];
+					lbuffer[i] = (Float_t)(scale * (double)s);
+					s = abs(s);
+					block_peak = local_max(block_peak, s);
+				}
+				samples -= n;
+				if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
+					return false;
+			}
+		}
+	}
+
+	{
+		const double peak_scale = (double)(1u << (bps - 1));
+		double peak = (double)block_peak / peak_scale;
+		if(peak > title_peak_)
+			title_peak_ = peak;
+		if(peak > album_peak_)
+			album_peak_ = peak;
+	}
+
+	return true;
+}
+
+void grabbag__replaygain_get_album(float *gain, float *peak)
+{
+	*gain = (float)GetAlbumGain();
+	*peak = (float)album_peak_;
+	album_peak_ = 0.0;
+}
+
+void grabbag__replaygain_get_title(float *gain, float *peak)
+{
+	*gain = (float)GetTitleGain();
+	*peak = (float)title_peak_;
+	title_peak_ = 0.0;
+}
+
+
+typedef struct {
+	unsigned channels;
+	unsigned bits_per_sample;
+	unsigned sample_rate;
+	FLAC__bool error;
+} DecoderInstance;
+
+static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+	DecoderInstance *instance = (DecoderInstance*)client_data;
+	const unsigned bits_per_sample = frame->header.bits_per_sample;
+	const unsigned channels = frame->header.channels;
+	const unsigned sample_rate = frame->header.sample_rate;
+	const unsigned samples = frame->header.blocksize;
+
+	(void)decoder;
+
+	if(
+		!instance->error &&
+		(channels == 2 || channels == 1) &&
+		bits_per_sample == instance->bits_per_sample &&
+		channels == instance->channels &&
+		sample_rate == instance->sample_rate
+	) {
+		instance->error = !grabbag__replaygain_analyze(buffer, channels==2, bits_per_sample, samples);
+	}
+	else {
+		instance->error = true;
+	}
+
+	if(!instance->error)
+		return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+	else
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+}
+
+static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+	DecoderInstance *instance = (DecoderInstance*)client_data;
+
+	(void)decoder;
+
+	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+		instance->bits_per_sample = metadata->data.stream_info.bits_per_sample;
+		instance->channels = metadata->data.stream_info.channels;
+		instance->sample_rate = metadata->data.stream_info.sample_rate;
+
+		if(instance->channels != 1 && instance->channels != 2) {
+			instance->error = true;
+			return;
+		}
+
+		if(!grabbag__replaygain_is_valid_sample_frequency(instance->sample_rate)) {
+			instance->error = true;
+			return;
+		}
+	}
+}
+
+static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+	DecoderInstance *instance = (DecoderInstance*)client_data;
+
+	(void)decoder, (void)status;
+
+	instance->error = true;
+}
+
+const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak)
+{
+	DecoderInstance instance;
+	FLAC__FileDecoder *decoder = FLAC__file_decoder_new();
+
+	if(0 == decoder)
+		return "memory allocation error";
+
+	instance.error = false;
+
+	/* It does these three by default but lets be explicit: */
+	FLAC__file_decoder_set_md5_checking(decoder, false);
+	FLAC__file_decoder_set_metadata_ignore_all(decoder);
+	FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
+
+	FLAC__file_decoder_set_filename(decoder, filename);
+	FLAC__file_decoder_set_write_callback(decoder, write_callback_);
+	FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
+	FLAC__file_decoder_set_error_callback(decoder, error_callback_);
+	FLAC__file_decoder_set_client_data(decoder, &instance);
+
+	if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
+		FLAC__file_decoder_delete(decoder);
+		return "initializing decoder";
+	}
+
+	if(!FLAC__file_decoder_process_until_end_of_file(decoder) || instance.error) {
+		FLAC__file_decoder_delete(decoder);
+		return "decoding file";
+	}
+
+	FLAC__file_decoder_delete(decoder);
+
+	grabbag__replaygain_get_title(title_gain, title_peak);
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak)
+{
+	const char *error;
+
+	if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak)))
+		return error;
+
+	if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak)))
+		return error;
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak)
+{
+	FLAC__ASSERT(0 != block);
+	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+	if(
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_gain_) < 0 ||
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_peak_) < 0
+	)
+		return "memory allocation error";
+
+	if(
+		!append_tag_(block, peak_format_, tag_album_peak_, album_peak) ||
+		!append_tag_(block, gain_format_, tag_album_gain_, album_gain)
+	)
+		return "memory allocation error";
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak)
+{
+	FLAC__ASSERT(0 != block);
+	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+	if(
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_gain_) < 0 ||
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_peak_) < 0
+	)
+		return "memory allocation error";
+
+	if(
+		!append_tag_(block, peak_format_, tag_title_peak_, title_peak) ||
+		!append_tag_(block, gain_format_, tag_title_gain_, title_gain)
+	)
+		return "memory allocation error";
+
+	return 0;
+}
+
+static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block)
+{
+	FLAC__Metadata_Iterator *iterator;
+	const char *error;
+	FLAC__bool found_vc_block = false;
+
+	if(0 == (*chain = FLAC__metadata_chain_new()))
+		return "memory allocation error";
+
+	if(!FLAC__metadata_chain_read(*chain, filename)) {
+		error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
+		FLAC__metadata_chain_delete(*chain);
+		return error;
+	}
+
+	if(0 == (iterator = FLAC__metadata_iterator_new())) {
+		FLAC__metadata_chain_delete(*chain);
+		return "memory allocation error";
+	}
+
+	FLAC__metadata_iterator_init(iterator, *chain);
+
+	do {
+		*block = FLAC__metadata_iterator_get_block(iterator);
+		if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+			found_vc_block = true;
+	} while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
+
+	if(!found_vc_block) {
+		/* create a new block */
+		*block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+		if(0 == *block) {
+			FLAC__metadata_chain_delete(*chain);
+			FLAC__metadata_iterator_delete(iterator);
+			return "memory allocation error";
+		}
+		while(FLAC__metadata_iterator_next(iterator))
+			;
+		if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) {
+			error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
+			FLAC__metadata_chain_delete(*chain);
+			FLAC__metadata_iterator_delete(iterator);
+			return error;
+		}
+		/* iterator is left pointing to new block */
+		FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block);
+	}
+
+	FLAC__metadata_iterator_delete(iterator);
+
+	FLAC__ASSERT(0 != *block);
+	FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+	return 0;
+}
+
+static const char *store_to_file_post_(const char *filename, FLAC__Metadata_Chain *chain, FLAC__bool preserve_modtime)
+{
+	struct stat stats;
+	const FLAC__bool have_stats = get_file_stats_(filename, &stats);
+
+	(void)grabbag__file_change_stats(filename, /*read_only=*/false);
+
+	FLAC__metadata_chain_sort_padding(chain);
+	if(!FLAC__metadata_chain_write(chain, /*use_padding=*/true, preserve_modtime)) {
+		FLAC__metadata_chain_delete(chain);
+		return FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)];
+	}
+
+	FLAC__metadata_chain_delete(chain);
+
+	if(have_stats)
+		set_file_stats_(filename, &stats);
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime)
+{
+	FLAC__Metadata_Chain *chain;
+	FLAC__StreamMetadata *block;
+	const char *error;
+
+	if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
+		return error;
+
+	if(0 != (error = grabbag__replaygain_store_to_vorbiscomment(block, album_gain, album_peak, title_gain, title_peak))) {
+		FLAC__metadata_chain_delete(chain);
+		return error;
+	}
+
+	if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
+		return error;
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime)
+{
+	FLAC__Metadata_Chain *chain;
+	FLAC__StreamMetadata *block;
+	const char *error;
+
+	if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
+		return error;
+
+	if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) {
+		FLAC__metadata_chain_delete(chain);
+		return error;
+	}
+
+	if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
+		return error;
+
+	return 0;
+}
+
+const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime)
+{
+	FLAC__Metadata_Chain *chain;
+	FLAC__StreamMetadata *block;
+	const char *error;
+
+	if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
+		return error;
+
+	if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) {
+		FLAC__metadata_chain_delete(chain);
+		return error;
+	}
+
+	if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
+		return error;
+
+	return 0;
+}
+
+static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *entry, double *val)
+{
+	char s[32], *end;
+	const char *p, *q;
+	double v;
+
+	FLAC__ASSERT(0 != entry);
+	FLAC__ASSERT(0 != val);
+
+	p = (const char *)entry->entry;
+	q = strchr(p, '=');
+	if(0 == q)
+		return false;
+	q++;
+	memset(s, 0, sizeof(s)-1);
+	strncpy(s, q, local_min(sizeof(s)-1, (size_t)(entry->length - (q-p))));
+
+	v = strtod(s, &end);
+	if(end == s)
+		return false;
+
+	*val = v;
+	return true;
+}
+
+FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, double *gain, double *peak)
+{
+	int gain_offset, peak_offset;
+
+	FLAC__ASSERT(0 != block);
+	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+	if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_gain_ : tag_title_gain_))))
+		return false;
+	if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_peak_ : tag_title_peak_))))
+		return false;
+
+	if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
+		return false;
+	if(!parse_double_(block->data.vorbis_comment.comments + peak_offset, peak))
+		return false;
+
+	return true;
+}
+
+double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping)
+{
+	double scale;
+	FLAC__ASSERT(peak >= 0.0);
+ 	gain += preamp;
+	scale = (float) pow(10.0, gain * 0.05);
+	if(prevent_clipping && peak > 0.0) {
+		const double max_scale = (float)(1.0 / peak);
+		if(scale > max_scale)
+			scale = max_scale;
+	}
+	return scale;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/replaygain_analysis.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,413 @@
+/*
+ *  ReplayGainAnalysis - analyzes input samples and give the recommended dB change
+ *  Copyright (C) 2001 David Robinson and Glen Sawyer
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  concept and filter values by David Robinson (David@Robinson.org)
+ *    -- blame him if you think the idea is flawed
+ *  original coding by Glen Sawyer (glensawyer@hotmail.com)
+ *    -- blame him if you think this runs too slowly, or the coding is otherwise flawed
+ *
+ *  lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ )
+ *    -- credit him for all the _good_ programming ;)
+ *
+ *  minor cosmetic tweaks to integrate with FLAC by Josh Coalson
+ *
+ *
+ *  For an explanation of the concepts and the basic algorithms involved, go to:
+ *    http://www.replaygain.org/
+ */
+
+/*
+ *  Here's the deal. Call
+ *
+ *    InitGainAnalysis ( long samplefreq );
+ *
+ *  to initialize everything. Call
+ *
+ *    AnalyzeSamples ( const Float_t*  left_samples,
+ *                     const Float_t*  right_samples,
+ *                     size_t          num_samples,
+ *                     int             num_channels );
+ *
+ *  as many times as you want, with as many or as few samples as you want.
+ *  If mono, pass the sample buffer in through left_samples, leave
+ *  right_samples NULL, and make sure num_channels = 1.
+ *
+ *    GetTitleGain()
+ *
+ *  will return the recommended dB level change for all samples analyzed
+ *  SINCE THE LAST TIME you called GetTitleGain() OR InitGainAnalysis().
+ *
+ *    GetAlbumGain()
+ *
+ *  will return the recommended dB level change for all samples analyzed
+ *  since InitGainAnalysis() was called and finalized with GetTitleGain().
+ *
+ *  Pseudo-code to process an album:
+ *
+ *    Float_t       l_samples [4096];
+ *    Float_t       r_samples [4096];
+ *    size_t        num_samples;
+ *    unsigned int  num_songs;
+ *    unsigned int  i;
+ *
+ *    InitGainAnalysis ( 44100 );
+ *    for ( i = 1; i <= num_songs; i++ ) {
+ *        while ( ( num_samples = getSongSamples ( song[i], left_samples, right_samples ) ) > 0 )
+ *            AnalyzeSamples ( left_samples, right_samples, num_samples, 2 );
+ *        fprintf ("Recommended dB change for song %2d: %+6.2f dB\n", i, GetTitleGain() );
+ *    }
+ *    fprintf ("Recommended dB change for whole album: %+6.2f dB\n", GetAlbumGain() );
+ */
+
+/*
+ *  So here's the main source of potential code confusion:
+ *
+ *  The filters applied to the incoming samples are IIR filters,
+ *  meaning they rely on up to <filter order> number of previous samples
+ *  AND up to <filter order> number of previous filtered samples.
+ *
+ *  I set up the AnalyzeSamples routine to minimize memory usage and interface
+ *  complexity. The speed isn't compromised too much (I don't think), but the
+ *  internal complexity is higher than it should be for such a relatively
+ *  simple routine.
+ *
+ *  Optimization/clarity suggestions are welcome.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "replaygain_analysis.h"
+
+typedef unsigned short  Uint16_t;
+typedef signed short    Int16_t;
+typedef unsigned int    Uint32_t;
+typedef signed int      Int32_t;
+
+#define YULE_ORDER         10
+#define BUTTER_ORDER        2
+#define RMS_PERCENTILE      0.95        /* percentile which is louder than the proposed level */
+#define MAX_SAMP_FREQ   48000.          /* maximum allowed sample frequency [Hz] */
+#define RMS_WINDOW_TIME     0.050       /* Time slice size [s] */
+#define STEPS_per_dB      100.          /* Table entries per dB */
+#define MAX_dB            120.          /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */
+
+#define MAX_ORDER               (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER)
+/* [JEC] the following was originally #defined as:
+ *   (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME)
+ * but that seemed to fail to take into account the ceil() part of the
+ * sampleWindow calculation in ResetSampleFrequency(), and was causing
+ * buffer overflows for 48kHz analysis, hence the +1.
+ */
+#define MAX_SAMPLES_PER_WINDOW  (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME + 1.)   /* max. Samples per Time slice */
+#define PINK_REF                64.82 /* 298640883795 */                          /* calibration value */
+
+static Float_t          linprebuf [MAX_ORDER * 2];
+static Float_t*         linpre;                                          /* left input samples, with pre-buffer */
+static Float_t          lstepbuf  [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+static Float_t*         lstep;                                           /* left "first step" (i.e. post first filter) samples */
+static Float_t          loutbuf   [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+static Float_t*         lout;                                            /* left "out" (i.e. post second filter) samples */
+static Float_t          rinprebuf [MAX_ORDER * 2];
+static Float_t*         rinpre;                                          /* right input samples ... */
+static Float_t          rstepbuf  [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+static Float_t*         rstep;
+static Float_t          routbuf   [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+static Float_t*         rout;
+static unsigned int              sampleWindow;                           /* number of samples required to reach number of milliseconds required for RMS window */
+static unsigned long    totsamp;
+static double           lsum;
+static double           rsum;
+static int              freqindex;
+static Uint32_t  A [(size_t)(STEPS_per_dB * MAX_dB)];
+static Uint32_t  B [(size_t)(STEPS_per_dB * MAX_dB)];
+
+/* for each filter:
+   [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz */
+
+#ifdef WIN32
+#pragma warning ( disable : 4305 )
+#endif
+
+static const Float_t  AYule [9] [11] = {
+    { 1., -3.84664617118067,  7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294,  9.48293806319790, -5.87257861775999,  2.75465861874613, -0.86984376593551, 0.13919314567432 },
+    { 1., -3.47845948550071,  6.36317777566148, -8.54751527471874,  9.47693607801280, -8.81498681370155,  6.85401540936998, -4.39470996079559,  2.19611684890774, -0.75104302451432, 0.13149317958808 },
+    { 1., -2.37898834973084,  2.84868151156327, -2.64577170229825,  2.23697657451713, -1.67148153367602,  1.00595954808547, -0.45953458054983,  0.16378164858596, -0.05032077717131, 0.02347897407020 },
+    { 1., -1.61273165137247,  1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906,  0.39120800788284, -0.22138138954925,  0.04500235387352,  0.02005851806501, 0.00302439095741 },
+    { 1., -1.49858979367799,  0.87350271418188,  0.12205022308084, -0.80774944671438,  0.47854794562326, -0.12453458140019, -0.04067510197014,  0.08333755284107, -0.04237348025746, 0.02977207319925 },
+    { 1., -0.62820619233671,  0.29661783706366, -0.37256372942400,  0.00213767857124, -0.42029820170918,  0.22199650564824,  0.00613424350682,  0.06747620744683,  0.05784820375801, 0.03222754072173 },
+    { 1., -1.04800335126349,  0.29156311971249, -0.26806001042947,  0.00819999645858,  0.45054734505008, -0.33032403314006,  0.06739368333110, -0.04784254229033,  0.01639907836189, 0.01807364323573 },
+    { 1., -0.51035327095184, -0.31863563325245, -0.20256413484477,  0.14728154134330,  0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053,  0.02442357316099, 0.01818801111503 },
+    { 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242,  0.26408300200955,  0.15113130533216, -0.17556493366449, -0.18823009262115,  0.05477720428674, 0.04704409688120 }
+};
+
+static const Float_t  BYule [9] [11] = {
+    { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619,  0.02161526843274, -0.02074045215285,  0.00594298065125,  0.00306428023191,  0.00012025322027,  0.00288463683916 },
+    { 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936,  0.02245293253339, -0.02596338512915,  0.01624864962975, -0.00240879051584,  0.00674613682247, -0.00187763777362 },
+    { 0.15457299681924, -0.09331049056315, -0.06247880153653,  0.02163541888798, -0.05588393329856,  0.04781476674921,  0.00222312597743,  0.03174092540049, -0.01390589421898,  0.00651420667831, -0.00881362733839 },
+    { 0.30296907319327, -0.22613988682123, -0.08587323730772,  0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913,  0.06276101321749, -0.00000828086748,  0.00205861885564, -0.02950134983287 },
+    { 0.33642304856132, -0.25572241425570, -0.11828570177555,  0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440,  0.05724228140351,  0.00832043980773, -0.01635381384540, -0.01760176568150 },
+    { 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551,  0.04078262797139, -0.12398163381748,  0.04097565135648,  0.10478503600251, -0.01863887810927, -0.03193428438915,  0.00541907748707 },
+    { 0.56619470757641, -0.75464456939302,  0.16242137742230,  0.16744243493672, -0.18901604199609,  0.30931782841830, -0.27562961986224,  0.00647310677246,  0.08647503780351, -0.03788984554840, -0.00588215443421 },
+    { 0.58100494960553, -0.53174909058578, -0.14289799034253,  0.17520704835522,  0.02377945217615,  0.15558449135573, -0.25344790059353,  0.01628462406333,  0.06920467763959, -0.03721611395801, -0.00749618797172 },
+    { 0.53648789255105, -0.42163034350696, -0.00275953611929,  0.04267842219415, -0.10214864179676,  0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000,  0.04788665548180, -0.02217936801134 }
+};
+
+static const Float_t  AButter [9] [3] = {
+    { 1., -1.97223372919527, 0.97261396931306 },
+    { 1., -1.96977855582618, 0.97022847566350 },
+    { 1., -1.95835380975398, 0.95920349965459 },
+    { 1., -1.95002759149878, 0.95124613669835 },
+    { 1., -1.94561023566527, 0.94705070426118 },
+    { 1., -1.92783286977036, 0.93034775234268 },
+    { 1., -1.91858953033784, 0.92177618768381 },
+    { 1., -1.91542108074780, 0.91885558323625 },
+    { 1., -1.88903307939452, 0.89487434461664 }
+};
+
+static const Float_t  BButter [9] [3] = {
+    { 0.98621192462708, -1.97242384925416, 0.98621192462708 },
+    { 0.98500175787242, -1.97000351574484, 0.98500175787242 },
+    { 0.97938932735214, -1.95877865470428, 0.97938932735214 },
+    { 0.97531843204928, -1.95063686409857, 0.97531843204928 },
+    { 0.97316523498161, -1.94633046996323, 0.97316523498161 },
+    { 0.96454515552826, -1.92909031105652, 0.96454515552826 },
+    { 0.96009142950541, -1.92018285901082, 0.96009142950541 },
+    { 0.95856916599601, -1.91713833199203, 0.95856916599601 },
+    { 0.94597685600279, -1.89195371200558, 0.94597685600279 }
+};
+
+#ifdef WIN32
+#pragma warning ( default : 4305 )
+#endif
+
+/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */
+
+static void
+filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order )
+{
+    double  y;
+    size_t  i;
+    size_t  k;
+
+    for ( i = 0; i < nSamples; i++ ) {
+        y = input[i] * b[0];
+        for ( k = 1; k <= order; k++ )
+            y += input[i-k] * b[k] - output[i-k] * a[k];
+        output[i] = (Float_t)y;
+    }
+}
+
+/* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */
+
+int
+ResetSampleFrequency ( long samplefreq ) {
+    int  i;
+
+    /* zero out initial values */
+    for ( i = 0; i < MAX_ORDER; i++ )
+        linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.;
+
+    switch ( (int)(samplefreq) ) {
+        case 48000: freqindex = 0; break;
+        case 44100: freqindex = 1; break;
+        case 32000: freqindex = 2; break;
+        case 24000: freqindex = 3; break;
+        case 22050: freqindex = 4; break;
+        case 16000: freqindex = 5; break;
+        case 12000: freqindex = 6; break;
+        case 11025: freqindex = 7; break;
+        case  8000: freqindex = 8; break;
+        default:    return INIT_GAIN_ANALYSIS_ERROR;
+    }
+
+    sampleWindow = (int) ceil (samplefreq * RMS_WINDOW_TIME);
+
+    lsum         = 0.;
+    rsum         = 0.;
+    totsamp      = 0;
+
+    memset ( A, 0, sizeof(A) );
+
+	return INIT_GAIN_ANALYSIS_OK;
+}
+
+int
+InitGainAnalysis ( long samplefreq )
+{
+	if (ResetSampleFrequency(samplefreq) != INIT_GAIN_ANALYSIS_OK) {
+		return INIT_GAIN_ANALYSIS_ERROR;
+	}
+
+    linpre       = linprebuf + MAX_ORDER;
+    rinpre       = rinprebuf + MAX_ORDER;
+    lstep        = lstepbuf  + MAX_ORDER;
+    rstep        = rstepbuf  + MAX_ORDER;
+    lout         = loutbuf   + MAX_ORDER;
+    rout         = routbuf   + MAX_ORDER;
+
+    memset ( B, 0, sizeof(B) );
+
+    return INIT_GAIN_ANALYSIS_OK;
+}
+
+/* returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not */
+
+int
+AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels )
+{
+    const Float_t*  curleft;
+    const Float_t*  curright;
+    long            batchsamples;
+    long            cursamples;
+    long            cursamplepos;
+    int             i;
+
+    if ( num_samples == 0 )
+        return GAIN_ANALYSIS_OK;
+
+    cursamplepos = 0;
+    batchsamples = num_samples;
+
+    switch ( num_channels) {
+    case  1: right_samples = left_samples;
+    case  2: break;
+    default: return GAIN_ANALYSIS_ERROR;
+    }
+
+    if ( num_samples < MAX_ORDER ) {
+        memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) );
+        memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) );
+    }
+    else {
+        memcpy ( linprebuf + MAX_ORDER, left_samples,  MAX_ORDER   * sizeof(Float_t) );
+        memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER   * sizeof(Float_t) );
+    }
+
+    while ( batchsamples > 0 ) {
+        cursamples = batchsamples > (long)(sampleWindow-totsamp)  ?  (long)(sampleWindow - totsamp)  :  batchsamples;
+        if ( cursamplepos < MAX_ORDER ) {
+            curleft  = linpre+cursamplepos;
+            curright = rinpre+cursamplepos;
+            if (cursamples > MAX_ORDER - cursamplepos )
+                cursamples = MAX_ORDER - cursamplepos;
+        }
+        else {
+            curleft  = left_samples  + cursamplepos;
+            curright = right_samples + cursamplepos;
+        }
+
+        filter ( curleft , lstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER );
+        filter ( curright, rstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER );
+
+        filter ( lstep + totsamp, lout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER );
+        filter ( rstep + totsamp, rout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER );
+
+        for ( i = 0; i < cursamples; i++ ) {             /* Get the squared values */
+            lsum += lout [totsamp+i] * lout [totsamp+i];
+            rsum += rout [totsamp+i] * rout [totsamp+i];
+        }
+
+        batchsamples -= cursamples;
+        cursamplepos += cursamples;
+        totsamp      += cursamples;
+        if ( totsamp == sampleWindow ) {  /* Get the Root Mean Square (RMS) for this set of samples */
+            double  val  = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 );
+            int     ival = (int) val;
+            if ( ival <                     0 ) ival = 0;
+            if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = (int)(sizeof(A)/sizeof(*A)) - 1;
+            A [ival]++;
+            lsum = rsum = 0.;
+            memmove ( loutbuf , loutbuf  + totsamp, MAX_ORDER * sizeof(Float_t) );
+            memmove ( routbuf , routbuf  + totsamp, MAX_ORDER * sizeof(Float_t) );
+            memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
+            memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
+            totsamp = 0;
+        }
+        if ( totsamp > sampleWindow )   /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */
+            return GAIN_ANALYSIS_ERROR;
+    }
+    if ( num_samples < MAX_ORDER ) {
+        memmove ( linprebuf,                           linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
+        memmove ( rinprebuf,                           rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
+        memcpy  ( linprebuf + MAX_ORDER - num_samples, left_samples,          num_samples             * sizeof(Float_t) );
+        memcpy  ( rinprebuf + MAX_ORDER - num_samples, right_samples,         num_samples             * sizeof(Float_t) );
+    }
+    else {
+        memcpy  ( linprebuf, left_samples  + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
+        memcpy  ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
+    }
+
+    return GAIN_ANALYSIS_OK;
+}
+
+
+static Float_t
+analyzeResult ( Uint32_t* Array, size_t len )
+{
+    Uint32_t  elems;
+    Int32_t   upper;
+    size_t    i;
+
+    elems = 0;
+    for ( i = 0; i < len; i++ )
+        elems += Array[i];
+    if ( elems == 0 )
+        return GAIN_NOT_ENOUGH_SAMPLES;
+
+    upper = (Int32_t) ceil (elems * (1. - RMS_PERCENTILE));
+    for ( i = len; i-- > 0; ) {
+        if ( (upper -= Array[i]) <= 0 )
+            break;
+    }
+
+    return (Float_t) ((Float_t)PINK_REF - (Float_t)i / (Float_t)STEPS_per_dB);
+}
+
+
+Float_t
+GetTitleGain ( void )
+{
+    Float_t  retval;
+    unsigned int    i;
+
+    retval = analyzeResult ( A, sizeof(A)/sizeof(*A) );
+
+    for ( i = 0; i < sizeof(A)/sizeof(*A); i++ ) {
+        B[i] += A[i];
+        A[i]  = 0;
+    }
+
+    for ( i = 0; i < MAX_ORDER; i++ )
+        linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.f;
+
+    totsamp = 0;
+    lsum    = rsum = 0.;
+    return retval;
+}
+
+
+Float_t
+GetAlbumGain ( void )
+{
+    return analyzeResult ( B, sizeof(B)/sizeof(*B) );
+}
+
+/* end of replaygain_analysis.c */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/replaygain_analysis.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,57 @@
+/*
+ *  ReplayGainAnalysis - analyzes input samples and give the recommended dB change
+ *  Copyright (C) 2001 David Robinson and Glen Sawyer
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  concept and filter values by David Robinson (David@Robinson.org)
+ *    -- blame him if you think the idea is flawed
+ *  coding by Glen Sawyer (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA
+ *    -- blame him if you think this runs too slowly, or the coding is otherwise flawed
+ *  minor cosmetic tweaks to integrate with FLAC by Josh Coalson
+ *
+ *  For an explanation of the concepts and the basic algorithms involved, go to:
+ *    http://www.replaygain.org/
+ */
+
+#ifndef GAIN_ANALYSIS_H
+#define GAIN_ANALYSIS_H
+
+#include <stddef.h>
+
+#define GAIN_NOT_ENOUGH_SAMPLES  -24601
+#define GAIN_ANALYSIS_ERROR           0
+#define GAIN_ANALYSIS_OK              1
+
+#define INIT_GAIN_ANALYSIS_ERROR      0
+#define INIT_GAIN_ANALYSIS_OK         1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef float   Float_t;         /* Type used for filtering */
+
+int     InitGainAnalysis ( long samplefreq );
+int     AnalyzeSamples   ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels );
+int		ResetSampleFrequency ( long samplefreq );
+Float_t   GetTitleGain     ( void );
+Float_t   GetAlbumGain     ( void );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GAIN_ANALYSIS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/replaygain_synthesis.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,465 @@
+/* replaygain_synthesis - Routines for applying ReplayGain to a signal
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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.
+ */
+/*
+ * This is an aggregation of pieces of code from John Edwards' WaveGain
+ * program.  Mostly cosmetic changes were made; otherwise, the dithering
+ * code is almost untouched and the gain processing was converted from
+ * processing a whole file to processing chunks of samples.
+ *
+ * The original copyright notices for WaveGain's dither.c and wavegain.c
+ * appear below:
+ */
+/*
+ * (c) 2002 John Edwards
+ * mostly lifted from work by Frank Klemm
+ * random functions for dithering.
+ */
+/*
+ * Copyright (C) 2002 John Edwards
+ * Additional code by Magnus Holmgren and Gian-Carlo Pascutto
+ */
+
+#include <string.h> /* for memset() */
+#include <math.h>
+#include "fast_float_math_hack.h"
+#include "replaygain_synthesis.h"
+#include "FLAC/assert.h"
+
+#if defined _MSC_VER
+#define FLAC__INLINE __inline
+#else
+#define FLAC__INLINE
+#endif
+
+/* adjust for compilers that can't understand using LL suffix for int64_t literals */
+#ifdef _MSC_VER
+#define FLAC__I64L(x) x
+#else
+#define FLAC__I64L(x) x##LL
+#endif
+
+
+/*
+ * the following is based on parts of dither.c
+ */
+
+
+/*
+ *  This is a simple random number generator with good quality for audio purposes.
+ *  It consists of two polycounters with opposite rotation direction and different
+ *  periods. The periods are coprime, so the total period is the product of both.
+ *
+ *     -------------------------------------------------------------------------------------------------
+ * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0|
+ * |   -------------------------------------------------------------------------------------------------
+ * |                                                                          |  |  |  |     |        |
+ * |                                                                          +--+--+--+-XOR-+--------+
+ * |                                                                                      |
+ * +--------------------------------------------------------------------------------------+
+ *
+ *     -------------------------------------------------------------------------------------------------
+ *     |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+
+ *     -------------------------------------------------------------------------------------------------   |
+ *       |  |           |  |                                                                               |
+ *       +--+----XOR----+--+                                                                               |
+ *                |                                                                                        |
+ *                +----------------------------------------------------------------------------------------+
+ *
+ *
+ *  The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481,
+ *  which gives a period of 18.410.713.077.675.721.215. The result is the
+ *  XORed values of both generators.
+ */
+
+static unsigned int random_int_()
+{
+	static const unsigned char parity_[256] = {
+		0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+		1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+		1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+		0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+		1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+		0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+		0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+		1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0
+	};
+	static unsigned int r1_ = 1;
+	static unsigned int r2_ = 1;
+
+	unsigned int t1, t2, t3, t4;
+
+	/* Parity calculation is done via table lookup, this is also available
+	 * on CPUs without parity, can be implemented in C and avoid unpredictable
+	 * jumps and slow rotate through the carry flag operations.
+	 */
+	t3   = t1 = r1_;    t4   = t2 = r2_;
+	t1  &= 0xF5;        t2 >>= 25;
+	t1   = parity_[t1]; t2  &= 0x63;
+	t1 <<= 31;          t2   = parity_[t2];
+
+	return (r1_ = (t3 >> 1) | t1 ) ^ (r2_ = (t4 + t4) | t2 );
+}
+
+/* gives a equal distributed random number */
+/* between -2^31*mult and +2^31*mult */
+static double random_equi_(double mult)
+{
+	return mult * (int) random_int_();
+}
+
+/* gives a triangular distributed random number */
+/* between -2^32*mult and +2^32*mult */
+static double random_triangular_(double mult)
+{
+	return mult * ( (double) (int) random_int_() + (double) (int) random_int_() );
+}
+
+
+static const float  F44_0 [16 + 32] = {
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0,
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0,
+
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0,
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0,
+
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0,
+	(float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0
+};
+
+
+static const float  F44_1 [16 + 32] = {  /* SNR(w) = 4.843163 dB, SNR = -3.192134 dB */
+	(float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833,
+	(float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967,
+	(float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116,
+	(float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024,
+
+	(float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833,
+	(float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967,
+	(float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116,
+	(float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024,
+
+	(float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833,
+	(float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967,
+	(float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116,
+	(float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024,
+};
+
+
+static const float  F44_2 [16 + 32] = {  /* SNR(w) = 10.060213 dB, SNR = -12.766730 dB */
+	(float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437,
+	(float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264,
+	(float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562,
+	(float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816,
+
+	(float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437,
+	(float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264,
+	(float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562,
+	(float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816,
+
+	(float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437,
+	(float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264,
+	(float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562,
+	(float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816,
+};
+
+
+static const float  F44_3 [16 + 32] = {  /* SNR(w) = 15.382598 dB, SNR = -29.402334 dB */
+	(float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515,
+	(float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785,
+	(float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927,
+	(float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099,
+
+	(float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515,
+	(float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785,
+	(float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927,
+	(float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099,
+
+	(float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515,
+	(float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785,
+	(float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927,
+	(float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099
+};
+
+
+static double scalar16_(const float* x, const float* y)
+{
+	return
+		x[ 0]*y[ 0] + x[ 1]*y[ 1] + x[ 2]*y[ 2] + x[ 3]*y[ 3] +
+		x[ 4]*y[ 4] + x[ 5]*y[ 5] + x[ 6]*y[ 6] + x[ 7]*y[ 7] +
+		x[ 8]*y[ 8] + x[ 9]*y[ 9] + x[10]*y[10] + x[11]*y[11] +
+		x[12]*y[12] + x[13]*y[13] + x[14]*y[14] + x[15]*y[15];
+}
+
+
+void FLAC__replaygain_synthesis__init_dither_context(DitherContext *d, int bits, int shapingtype)
+{
+	static unsigned char default_dither [] = { 92, 92, 88, 84, 81, 78, 74, 67,  0,  0 };
+	static const float*               F [] = { F44_0, F44_1, F44_2, F44_3 };
+
+	int index;
+
+	if (shapingtype < 0) shapingtype = 0;
+	if (shapingtype > 3) shapingtype = 3;
+	d->ShapingType = (NoiseShaping)shapingtype;
+	index = bits - 11 - shapingtype;
+	if (index < 0) index = 0;
+	if (index > 9) index = 9;
+
+	memset ( d->ErrorHistory , 0, sizeof (d->ErrorHistory ) );
+	memset ( d->DitherHistory, 0, sizeof (d->DitherHistory) );
+
+	d->FilterCoeff = F [shapingtype];
+	d->Mask   = ((FLAC__uint64)-1) << (32 - bits);
+	d->Add    = 0.5     * ((1L << (32 - bits)) - 1);
+	d->Dither = 0.01f*default_dither[index] / (((FLAC__int64)1) << bits);
+	d->LastHistoryIndex = 0;
+}
+
+/*
+ * the following is based on parts of wavegain.c
+ */
+
+static FLAC__INLINE FLAC__int64 dither_output_(DitherContext *d, FLAC__bool do_dithering, int shapingtype, int i, double Sum, int k)
+{
+	union {
+		double d;
+		FLAC__int64 i;
+	} doubletmp;
+	double Sum2;
+	FLAC__int64 val;
+
+#define ROUND64(x)   ( doubletmp.d = (x) + d->Add + (FLAC__int64)FLAC__I64L(0x001FFFFD80000000), doubletmp.i - (FLAC__int64)FLAC__I64L(0x433FFFFD80000000) )
+
+	if(do_dithering) {
+		if(shapingtype == 0) {
+			double  tmp = random_equi_(d->Dither);
+			Sum2 = tmp - d->LastRandomNumber [k];
+			d->LastRandomNumber [k] = (int)tmp;
+			Sum2 = Sum += Sum2;
+			val = ROUND64(Sum2) & d->Mask;
+		}
+		else {
+			Sum2 = random_triangular_(d->Dither) - scalar16_(d->DitherHistory[k], d->FilterCoeff + i);
+			Sum += d->DitherHistory [k] [(-1-i)&15] = (float)Sum2;
+			Sum2 = Sum + scalar16_(d->ErrorHistory [k], d->FilterCoeff + i);
+			val = ROUND64(Sum2) & d->Mask;
+			d->ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val);
+		}
+		return val;
+	}
+	else
+		return ROUND64(Sum);
+
+#undef ROUND64
+}
+
+#if 0
+	float        peak = 0.f,
+	             new_peak,
+	             factor_clip
+	double       scale,
+	             dB;
+
+	...
+
+	peak is in the range -32768.0 .. 32767.0
+
+	/* calculate factors for ReplayGain and ClippingPrevention */
+	*track_gain = GetTitleGain() + settings->man_gain;
+	scale = (float) pow(10., *track_gain * 0.05);
+	if(settings->clip_prev) {
+		factor_clip  = (float) (32767./( peak + 1));
+		if(scale < factor_clip)
+			factor_clip = 1.f;
+		else
+			factor_clip /= scale;
+		scale *= factor_clip;
+	}
+	new_peak = (float) peak * scale;
+
+	dB = 20. * log10(scale);
+	*track_gain = (float) dB;
+
+ 	const double scale = pow(10., (double)gain * 0.05);
+#endif
+
+
+size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context)
+{
+	static const FLAC__int32 conv_factors_[33] = {
+		-1, /* 0 bits-per-sample (not supported) */
+		-1, /* 1 bits-per-sample (not supported) */
+		-1, /* 2 bits-per-sample (not supported) */
+		-1, /* 3 bits-per-sample (not supported) */
+		268435456, /* 4 bits-per-sample */
+		134217728, /* 5 bits-per-sample */
+		67108864, /* 6 bits-per-sample */
+		33554432, /* 7 bits-per-sample */
+		16777216, /* 8 bits-per-sample */
+		8388608, /* 9 bits-per-sample */
+		4194304, /* 10 bits-per-sample */
+		2097152, /* 11 bits-per-sample */
+		1048576, /* 12 bits-per-sample */
+		524288, /* 13 bits-per-sample */
+		262144, /* 14 bits-per-sample */
+		131072, /* 15 bits-per-sample */
+		65536, /* 16 bits-per-sample */
+		32768, /* 17 bits-per-sample */
+		16384, /* 18 bits-per-sample */
+		8192, /* 19 bits-per-sample */
+		4096, /* 20 bits-per-sample */
+		2048, /* 21 bits-per-sample */
+		1024, /* 22 bits-per-sample */
+		512, /* 23 bits-per-sample */
+		256, /* 24 bits-per-sample */
+		128, /* 25 bits-per-sample */
+		64, /* 26 bits-per-sample */
+		32, /* 27 bits-per-sample */
+		16, /* 28 bits-per-sample */
+		8, /* 29 bits-per-sample */
+		4, /* 30 bits-per-sample */
+		2, /* 31 bits-per-sample */
+		1 /* 32 bits-per-sample */
+	};
+	static const FLAC__int64 hard_clip_factors_[33] = {
+		0, /* 0 bits-per-sample (not supported) */
+		0, /* 1 bits-per-sample (not supported) */
+		0, /* 2 bits-per-sample (not supported) */
+		0, /* 3 bits-per-sample (not supported) */
+		-8, /* 4 bits-per-sample */
+		-16, /* 5 bits-per-sample */
+		-32, /* 6 bits-per-sample */
+		-64, /* 7 bits-per-sample */
+		-128, /* 8 bits-per-sample */
+		-256, /* 9 bits-per-sample */
+		-512, /* 10 bits-per-sample */
+		-1024, /* 11 bits-per-sample */
+		-2048, /* 12 bits-per-sample */
+		-4096, /* 13 bits-per-sample */
+		-8192, /* 14 bits-per-sample */
+		-16384, /* 15 bits-per-sample */
+		-32768, /* 16 bits-per-sample */
+		-65536, /* 17 bits-per-sample */
+		-131072, /* 18 bits-per-sample */
+		-262144, /* 19 bits-per-sample */
+		-524288, /* 20 bits-per-sample */
+		-1048576, /* 21 bits-per-sample */
+		-2097152, /* 22 bits-per-sample */
+		-4194304, /* 23 bits-per-sample */
+		-8388608, /* 24 bits-per-sample */
+		-16777216, /* 25 bits-per-sample */
+		-33554432, /* 26 bits-per-sample */
+		-67108864, /* 27 bits-per-sample */
+		-134217728, /* 28 bits-per-sample */
+		-268435456, /* 29 bits-per-sample */
+		-536870912, /* 30 bits-per-sample */
+		-1073741824, /* 31 bits-per-sample */
+		(FLAC__int64)(-1073741824) * 2 /* 32 bits-per-sample */
+	};
+	const FLAC__int32 conv_factor = conv_factors_[target_bps];
+	const FLAC__int64 hard_clip_factor = hard_clip_factors_[target_bps];
+	/*
+	 * The integer input coming in has a varying range based on the
+	 * source_bps.  We want to normalize it to [-1.0, 1.0) so instead
+	 * of doing two multiplies on each sample, we just multiple
+	 * 'scale' by 1/(2^(source_bps-1))
+	 */
+	const double multi_scale = scale / (double)(1u << (source_bps-1));
+
+	FLAC__byte * const start = data_out;
+	unsigned i, channel;
+	const FLAC__int32 *input_;
+	double sample;
+	const unsigned bytes_per_sample = target_bps / 8;
+	const unsigned last_history_index = dither_context->LastHistoryIndex;
+	NoiseShaping noise_shaping = dither_context->ShapingType;
+	FLAC__int64 val64;
+	FLAC__int32 val32;
+	FLAC__int32 uval32;
+	const FLAC__uint32 twiggle = 1u << (target_bps - 1);
+
+	FLAC__ASSERT(channels > 0 && channels <= FLAC_SHARE__MAX_SUPPORTED_CHANNELS);
+	FLAC__ASSERT(source_bps >= 4);
+	FLAC__ASSERT(target_bps >= 4);
+	FLAC__ASSERT(source_bps <= 32);
+	FLAC__ASSERT(target_bps < 32);
+	FLAC__ASSERT((target_bps & 7) == 0);
+
+	for(channel = 0; channel < channels; channel++) {
+		const unsigned incr = bytes_per_sample * channels;
+		data_out = start + bytes_per_sample * channel;
+		input_ = input[channel];
+		for(i = 0; i < wide_samples; i++, data_out += incr) {
+			sample = (double)input_[i] * multi_scale;
+
+			if(hard_limit) {
+				/* hard 6dB limiting */
+				if(sample < -0.5)
+					sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5;
+				else if(sample > 0.5)
+					sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5;
+			}
+			sample *= 2147483647.f;
+
+			val64 = dither_output_(dither_context, do_dithering, noise_shaping, (i + last_history_index) % 32, sample, channel) / conv_factor;
+
+			val32 = (FLAC__int32)val64;
+			if(val64 >= -hard_clip_factor)
+				val32 = (FLAC__int32)(-(hard_clip_factor+1));
+			else if(val64 < hard_clip_factor)
+				val32 = (FLAC__int32)hard_clip_factor;
+
+			uval32 = (FLAC__uint32)val32;
+			if (unsigned_data_out)
+				uval32 ^= twiggle;
+
+			if (little_endian_data_out) {
+				switch(target_bps) {
+					case 24:
+						data_out[2] = (FLAC__byte)(uval32 >> 16);
+						/* fall through */
+					case 16:
+						data_out[1] = (FLAC__byte)(uval32 >> 8);
+						/* fall through */
+					case 8:
+						data_out[0] = (FLAC__byte)uval32;
+						break;
+				}
+			}
+			else {
+				switch(target_bps) {
+					case 24:
+						data_out[0] = (FLAC__byte)(uval32 >> 16);
+						data_out[1] = (FLAC__byte)(uval32 >> 8);
+						data_out[2] = (FLAC__byte)uval32;
+						break;
+					case 16:
+						data_out[0] = (FLAC__byte)(uval32 >> 8);
+						data_out[1] = (FLAC__byte)uval32;
+						break;
+					case 8:
+						data_out[0] = (FLAC__byte)uval32;
+						break;
+				}
+			}
+		}
+	}
+	dither_context->LastHistoryIndex = (last_history_index + wide_samples) % 32;
+
+	return wide_samples * channels * (target_bps/8);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/replaygain_synthesis.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,51 @@
+/* replaygain_synthesis - Routines for applying ReplayGain to a signal
+ * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ *
+ * 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 FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H
+#define FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H
+
+#include <stdlib.h> /* for size_t */
+#include "FLAC/ordinals.h"
+
+#define FLAC_SHARE__MAX_SUPPORTED_CHANNELS 2
+
+typedef enum {
+	NOISE_SHAPING_NONE = 0,
+	NOISE_SHAPING_LOW = 1,
+	NOISE_SHAPING_MEDIUM = 2,
+	NOISE_SHAPING_HIGH = 3
+} NoiseShaping;
+
+typedef struct {
+	const float*  FilterCoeff;
+	FLAC__uint64  Mask;
+	double        Add;
+	float         Dither;
+	float         ErrorHistory     [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16];  /* 16th order Noise shaping */
+	float         DitherHistory    [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16];
+	int           LastRandomNumber [FLAC_SHARE__MAX_SUPPORTED_CHANNELS];
+	unsigned      LastHistoryIndex;
+	NoiseShaping  ShapingType;
+} DitherContext;
+
+void FLAC__replaygain_synthesis__init_dither_context(DitherContext *dither, int bits, int shapingtype);
+
+/* scale = (float) pow(10., (double)replaygain * 0.05); */
+size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/tag.c	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,149 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * Based on FLAC plugin.c and mpg123 plugin
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <audacious/plugin.h>
+#include <audacious/util.h>
+#include <audacious/titlestring.h>
+
+#include "FLAC/metadata.h"
+#include "plugin_common/tags.h"
+#include "charset.h"
+#include "configure.h"
+
+/*
+ * Function local__extname (filename)
+ *
+ *    Return pointer within filename to its extenstion, or NULL if
+ *    filename has no extension.
+ *
+ */
+static char *local__extname(const char *filename)
+{
+	char *ext = strrchr(filename, '.');
+
+	if (ext != NULL)
+		++ext;
+
+	return ext;
+}
+
+static char *local__getstr(char* str)
+{
+	if (str && strlen(str) > 0)
+		return g_strdup(str);
+	return NULL;
+}
+
+static int local__getnum(char* str)
+{
+	if (str && strlen(str) > 0)
+		return atoi(str);
+	return 0;
+}
+
+static char *local__getfield(const FLAC__StreamMetadata *tags, const char *name)
+{
+	if (0 != tags) {
+		const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name);
+		if (0 != utf8) {
+			if(flac_cfg.title.convert_char_set)
+				return convert_from_utf8_to_user(utf8);
+			else
+				return strdup(utf8);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Function flac_format_song_title (tag, filename)
+ *
+ *    Create song title according to `tag' and/or `filename' and
+ *    return it.  The title must be subsequently freed using g_free().
+ *
+ */
+TitleInput *flac_get_tuple(char *filename)
+{
+	TitleInput *input = NULL;
+	FLAC__StreamMetadata *tags;
+	FLAC__StreamMetadata info;
+	char *title, *artist, *performer, *album, *date, *tracknumber, *genre, *description;
+	gchar *filename_proxy = g_strdup(filename);
+
+	FLAC_plugin__tags_get(filename_proxy, &tags);
+
+	title       = local__getfield(tags, "TITLE");
+	artist      = local__getfield(tags, "ARTIST");
+	performer   = local__getfield(tags, "PERFORMER");
+	album       = local__getfield(tags, "ALBUM");
+	date        = local__getfield(tags, "DATE");
+	tracknumber = local__getfield(tags, "TRACKNUMBER");
+	genre       = local__getfield(tags, "GENRE");
+	description = local__getfield(tags, "DESCRIPTION");
+
+	input = bmp_title_input_new();
+
+	input->performer = local__getstr(performer);
+	if(!input->performer)
+		input->performer = local__getstr(artist);
+	input->album_name = local__getstr(album);
+	input->track_name = local__getstr(title);
+	input->track_number = local__getnum(tracknumber);
+	input->year = local__getnum(date);
+	input->genre = local__getstr(genre);
+	input->comment = local__getstr(description);
+
+	input->file_name = g_path_get_basename(filename_proxy);
+	input->file_path = g_path_get_dirname(filename_proxy);
+	input->file_ext = local__extname(filename_proxy);
+
+        FLAC__metadata_get_streaminfo(filename, &info);
+
+	input->length = (unsigned)((double)info.data.stream_info.total_samples / (double)info.data.stream_info.sample_rate * 1000.0 + 0.5);
+
+	return input;
+}
+
+gchar *flac_format_song_title(gchar *filename)
+{
+	gchar *ret = NULL;
+	TitleInput *tuple = flac_get_tuple(filename);
+
+	ret = xmms_get_titlestring(flac_cfg.title.tag_override ? flac_cfg.title.tag_format : xmms_get_gentitle_format(), tuple);
+
+	if (!ret) {
+		/*
+		 * Format according to filename.
+		 */
+		ret = g_strdup(g_basename(filename));
+		if (local__extname(ret) != NULL)
+			*(local__extname(ret) - 1) = '\0';	/* removes period */
+	}
+
+	bmp_title_input_free(tuple);
+
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flac112/tag.h	Mon Oct 23 19:55:09 2006 -0700
@@ -0,0 +1,25 @@
+/* libxmms-flac - XMMS FLAC input plugin
+ * Copyright (C) 2002,2003,2004,2005  Daisuke Shimamura
+ *
+ * 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 __FLAC__PLUGIN_XMMS__TAG_H__
+#define __FLAC__PLUGIN_XMMS__TAG_H__
+
+TitleInput *flac_get_tuple(gchar * filename);
+gchar *flac_format_song_title(gchar * filename);
+
+#endif