changeset 610:862190d39e00 trunk

[svn] - add madplug. It is not yet hooked up, I'll do that later.
author nenolod
date Mon, 05 Feb 2007 12:28:01 -0800
parents 9b73eb35f4ff
children 3f7a52adfe0e
files ChangeLog src/madplug/Makefile src/madplug/configure.c src/madplug/decoder.c src/madplug/dither.c src/madplug/fileinfo.c src/madplug/input.c src/madplug/input.h src/madplug/mp3.xpm src/madplug/plugin.c src/madplug/plugin.h src/madplug/replaygain.c src/madplug/replaygain.h src/madplug/xing.c src/madplug/xing.h
diffstat 15 files changed, 4261 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Feb 04 16:43:27 2007 -0800
+++ b/ChangeLog	Mon Feb 05 12:28:01 2007 -0800
@@ -1,3 +1,10 @@
+2007-02-05 00:43:27 +0000  Giacomo Lozito <james@develia.org>
+  revision [1304]
+  - aosd: avoid collision between playback start trigger and titlechange trigger, that occurred when the next file in playlist was played
+  trunk/src/aosd/aosd_trigger.c |    4 ++++
+  1 file changed, 4 insertions(+)
+
+
 2007-02-04 21:40:53 +0000  Tony Vroon <chainsaw@gentoo.org>
   revision [1302]
   Obliterate empty tags, just like in mpg123.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/Makefile	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,25 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = libmadplug$(SHARED_SUFFIX)
+
+LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR)
+
+SOURCES = \
+  configure.c \
+  dither.c \
+  input.c \
+  replaygain.c \
+  decoder.c \
+  fileinfo.c \
+  plugin.c \
+  xing.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS   += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(TAGLIB_CFLAGS) $(ARCH_DEFINES) $(LIBNMS_CFLAGS) -I../../intl -I../..
+CXXFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(TAGLIB_CFLAGS) $(ARCH_DEFINES) -I../../intl -I../..
+
+LIBADD = -lmad -lid3tag $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(TAGLIB_LIBS) $(LIBNMS_LIBS)
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/configure.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,251 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "plugin.h"
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <audacious/configdb.h>
+
+static GtkWidget *configure_win = NULL;
+static GtkWidget *vbox;
+static GtkWidget *fast_playback, *use_xing, *dither;
+static GtkWidget *RG_enable, *RG_track_mode, *RG_default, *pregain,
+    *hard_limit;
+static GtkWidget *title_id3_box, *title_tag_desc;
+static GtkWidget *title_override, *title_id3_entry;
+
+static void configure_win_ok(GtkWidget * widget, gpointer data)
+{
+    ConfigDb *db;
+    const gchar *text = NULL;
+#ifdef DEBUG
+    g_message("saving\n");
+#endif
+
+    audmad_config.fast_play_time_calc =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fast_playback));
+    audmad_config.use_xing =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_xing));
+    audmad_config.dither =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dither));
+
+    audmad_config.replaygain.enable =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_enable));
+    audmad_config.replaygain.track_mode =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_track_mode));
+    audmad_config.hard_limit =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hard_limit));
+    text = gtk_entry_get_text(GTK_ENTRY(RG_default));
+    audmad_config.replaygain.default_db = g_strdup(text);
+
+    text = gtk_entry_get_text(GTK_ENTRY(pregain));
+    audmad_config.pregain_db = g_strdup(text);
+
+    audmad_config_compute(&audmad_config);
+
+    db = bmp_cfg_db_open();
+    bmp_cfg_db_set_int(db, "MAD", "http_buffer_size",
+                       audmad_config.http_buffer_size);
+    bmp_cfg_db_set_bool(db, "MAD", "fast_play_time_calc",
+                        audmad_config.fast_play_time_calc);
+    bmp_cfg_db_set_bool(db, "MAD", "use_xing", audmad_config.use_xing);
+    bmp_cfg_db_set_bool(db, "MAD", "dither", audmad_config.dither);
+    bmp_cfg_db_set_bool(db, "MAD", "hard_limit",
+                        audmad_config.hard_limit);
+    bmp_cfg_db_set_string(db, "MAD", "pregain_db",
+                          audmad_config.pregain_db);
+
+    bmp_cfg_db_set_bool(db, "MAD", "RG.enable",
+                        audmad_config.replaygain.enable);
+    bmp_cfg_db_set_bool(db, "MAD", "RG.track_mode",
+                        audmad_config.replaygain.track_mode);
+    bmp_cfg_db_set_string(db, "MAD", "RG.default_db",
+                          audmad_config.replaygain.default_db);
+
+    bmp_cfg_db_set_bool(db, "MAD", "title_override", audmad_config.title_override);
+    bmp_cfg_db_set_string(db, "MAD", "id3_format", audmad_config.id3_format);
+
+    bmp_cfg_db_close(db);
+    gtk_widget_destroy(configure_win);
+}
+
+static void configure_destroy(GtkWidget * w, gpointer data)
+{
+}
+
+static void
+title_override_cb(GtkWidget * w, gpointer data)
+{
+    gboolean override;
+    override =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_override));
+    gtk_widget_set_sensitive(title_id3_box, override);
+    gtk_widget_set_sensitive(title_tag_desc, override);
+}
+
+void audmad_configure(void)
+{
+    GtkWidget *bbox, *ok, *cancel;
+    GtkWidget *label, *RG_default_hbox, *pregain_hbox;
+    GtkWidget *notebook, *vbox2, *title_id3_label;
+
+    if (configure_win != NULL)
+    {
+        gtk_widget_show(configure_win);
+        return;
+    }
+
+    configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_type_hint(GTK_WINDOW(configure_win), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    g_signal_connect(G_OBJECT(configure_win), "destroy",
+                       G_CALLBACK(gtk_widget_destroyed),
+                       &configure_win);
+    g_signal_connect(G_OBJECT(configure_win), "destroy",
+                       G_CALLBACK(configure_destroy), &configure_win);
+
+    gtk_window_set_title(GTK_WINDOW(configure_win),
+                         _("MPEG Audio Plugin Configuration"));
+    gtk_window_set_policy(GTK_WINDOW(configure_win), FALSE, FALSE, FALSE);
+    gtk_container_border_width(GTK_CONTAINER(configure_win), 10);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(configure_win), vbox);
+
+    notebook = gtk_notebook_new();
+    gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+    vbox2 = gtk_vbox_new(FALSE, 5);
+
+    fast_playback =
+        gtk_check_button_new_with_label(_("Enable fast play-length calculation"));
+    gtk_box_pack_start(GTK_BOX(vbox2), fast_playback, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_playback),
+                                 audmad_config.fast_play_time_calc);
+
+    use_xing = gtk_check_button_new_with_label(_("Parse XING headers"));
+    gtk_box_pack_start(GTK_BOX(vbox2), use_xing, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_xing),
+                                 audmad_config.use_xing);
+
+    dither =
+        gtk_check_button_new_with_label
+        (_("Dither output when rounding to 16-bit"));
+    gtk_box_pack_start(GTK_BOX(vbox2), dither, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dither),
+                                 audmad_config.dither);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(_("General")));
+
+    vbox2 = gtk_vbox_new(FALSE, 5);
+
+    /* SKR added config : */
+    RG_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing"));
+    gtk_box_pack_start(GTK_BOX(vbox2), RG_enable, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_enable),
+                                 audmad_config.replaygain.enable);
+    RG_track_mode =
+        gtk_check_button_new_with_label(_("Track mode"));
+    gtk_box_pack_start(GTK_BOX(vbox2), RG_track_mode, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_track_mode),
+                                 audmad_config.replaygain.track_mode);
+
+    hard_limit =
+        gtk_check_button_new_with_label
+        (_("6dB hard limiting"));
+    gtk_box_pack_start(GTK_BOX(vbox2), hard_limit, TRUE, TRUE, 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hard_limit),
+                                 audmad_config.hard_limit);
+
+    label = gtk_label_new(_("Default gain (dB):"));
+    RG_default_hbox = gtk_hbox_new(FALSE, 5);
+    gtk_box_pack_start(GTK_BOX(vbox2), RG_default_hbox, TRUE, TRUE, 0);
+    RG_default = gtk_entry_new();
+    gtk_widget_set_usize(RG_default, 80, -1);
+    gtk_entry_set_text(GTK_ENTRY(RG_default),
+                       audmad_config.replaygain.default_db);
+    gtk_box_pack_start(GTK_BOX(RG_default_hbox), label, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(RG_default_hbox), RG_default, FALSE, TRUE,
+                       0);
+
+    label = gtk_label_new(_("Preamp (dB):"));
+    pregain_hbox = gtk_hbox_new(FALSE, 5);
+    gtk_box_pack_start(GTK_BOX(vbox2), pregain_hbox, TRUE, TRUE, 0);
+    pregain = gtk_entry_new();
+    gtk_widget_set_usize(pregain, 80, -1);
+    gtk_entry_set_text(GTK_ENTRY(pregain), audmad_config.pregain_db);
+    gtk_box_pack_start(GTK_BOX(pregain_hbox), label, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(pregain_hbox), pregain, FALSE, TRUE, 0);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new("ReplayGain"));
+
+    vbox2 = gtk_vbox_new(FALSE, 5);
+
+    title_override =
+        gtk_check_button_new_with_label(_("Override generic titles"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_override),
+                                 audmad_config.title_override);
+    g_signal_connect(G_OBJECT(title_override), "clicked",
+                     G_CALLBACK(title_override_cb), NULL);
+    gtk_box_pack_start(GTK_BOX(vbox2), title_override, FALSE,
+                       FALSE, 0);
+
+    title_id3_box = gtk_hbox_new(FALSE, 5);
+    gtk_widget_set_sensitive(title_id3_box, audmad_config.title_override);
+    gtk_box_pack_start(GTK_BOX(vbox2), title_id3_box, FALSE,
+                       FALSE, 0);
+
+    title_id3_label = gtk_label_new(_("ID3 format:"));
+    gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_label, FALSE,
+                       FALSE, 0);
+
+    title_id3_entry = gtk_entry_new();
+    gtk_entry_set_text(GTK_ENTRY(title_id3_entry), audmad_config.id3_format);
+    gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_entry, TRUE, TRUE,
+                       0);
+
+    title_tag_desc = xmms_titlestring_descriptions("pafFetnygc", 2);
+    gtk_widget_set_sensitive(title_tag_desc, audmad_config.title_override);
+    gtk_box_pack_start(GTK_BOX(vbox2), title_tag_desc, FALSE,
+                       FALSE, 0);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2,
+                             gtk_label_new(_("Title")));
+
+    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);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    g_signal_connect_swapped(G_OBJECT(cancel), "clicked",
+                              G_CALLBACK(gtk_widget_destroy),
+                              G_OBJECT(configure_win));
+    gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+    ok = gtk_button_new_from_stock(GTK_STOCK_OK);
+    g_signal_connect(G_OBJECT(ok), "clicked",
+                       G_CALLBACK(configure_win_ok), NULL);
+    gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
+    gtk_widget_grab_default(ok);
+
+    gtk_widget_show_all(configure_win);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/decoder.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,567 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <math.h>
+#include <assert.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include <audacious/plugin.h>
+#include <audacious/output.h>
+#include <audacious/util.h>
+#include "plugin.h"
+#include "input.h"
+
+#define BUFFER_SIZE 16*1024
+#define N_AVERAGE_FRAMES 10
+
+extern long triangular_dither_noise(int nbits);
+
+/**
+ * Scale PCM data
+ */
+static inline signed int
+scale(mad_fixed_t sample, struct mad_info_t *file_info)
+{
+    /* replayGain by SamKR */
+    gdouble scale = -1;
+    if (audmad_config.replaygain.enable) {
+        if (file_info->has_replaygain) {
+            scale = file_info->replaygain_track_scale;
+            if (file_info->replaygain_album_scale != -1
+                && (scale == -1 || !audmad_config.replaygain.track_mode))
+            {
+                scale = file_info->replaygain_album_scale;
+            }
+        }
+        if (scale == -1)
+            scale = audmad_config.replaygain.default_scale;
+    }
+    if (scale == -1)
+        scale = 1.0;
+    if (audmad_config.pregain_scale != 1)
+        scale = scale * audmad_config.pregain_scale;
+
+    /* hard-limit (clipping-prevention) */
+    if (audmad_config.hard_limit) {
+        /* convert to double before computation, to avoid mad_fixed_t wrapping */
+        double x = mad_f_todouble(sample) * scale;
+        static const double k = 0.5;    // -6dBFS
+        if (x > k) {
+            x = tanh((x - k) / (1 - k)) * (1 - k) + k;
+        }
+        else if (x < -k) {
+            x = tanh((x + k) / (1 - k)) * (1 - k) - k;
+        }
+        sample = x * (MAD_F_ONE);
+    }
+    else
+        sample *= scale;
+
+    int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16;
+
+    /* round */
+    /* add half of the bits_to_loose range to round */
+    sample += (1L << (n_bits_to_loose - 1));
+
+#ifdef DEBUG_DITHER
+    mad_fixed_t no_dither = sample;
+#endif
+
+    /* dither one bit of actual output */
+    if (audmad_config.dither) {
+        int dither = triangular_dither_noise(n_bits_to_loose + 1);
+        sample += dither;
+    }
+
+    /* clip */
+    /* make sure we are between -1 and 1 */
+    if (sample >= MAD_F_ONE) {
+        sample = MAD_F_ONE - 1;
+    }
+    else if (sample < -MAD_F_ONE) {
+        sample = -MAD_F_ONE;
+    }
+
+    /* quantize */
+    /*
+     * Turn our mad_fixed_t into an integer.
+     * Shift all but 16-bits of the fractional part
+     * off the right hand side and shift an extra place
+     * to get the sign bit.
+     */
+    sample >>= n_bits_to_loose;
+#ifdef DEBUG_DITHER
+    static int n_zeros = 0;
+    no_dither >>= n_bits_to_loose;
+    if (no_dither - sample == 0)
+        n_zeros++;
+    else {
+        g_message("dither: %d %d", n_zeros, no_dither - sample);
+        n_zeros = 0;
+    }
+#endif
+    return sample;
+}
+
+void
+write_output(struct mad_info_t *info, struct mad_pcm *pcm,
+             struct mad_header *header)
+{
+    unsigned int nsamples;
+    mad_fixed_t const *left_ch, *right_ch;
+    char *output;
+    int olen = 0;
+    int pos = 0;
+
+    nsamples = pcm->length;
+    left_ch = pcm->samples[0];
+    right_ch = pcm->samples[1];
+    olen = nsamples * MAD_NCHANNELS(header) * 2;
+    output = (char *) g_malloc(olen * sizeof(char));
+
+    while (nsamples--) {
+        signed int sample;
+        /* output sample(s) in 16-bit signed little-endian PCM */
+        sample = scale(*left_ch++, info);
+        output[pos++] = (sample >> 0) & 0xff;
+        output[pos++] = (sample >> 8) & 0xff;
+
+        if (MAD_NCHANNELS(header) == 2) {
+            sample = scale(*right_ch++, info);
+            output[pos++] = (sample >> 0) & 0xff;
+            output[pos++] = (sample >> 8) & 0xff;
+        }
+    }
+    assert(pos == olen);
+    if (info->playback->playing == 0)
+        return;
+    produce_audio(mad_plugin->output->written_time(),
+                  FMT_S16_LE, MAD_NCHANNELS(header), olen, output, NULL);
+    if (info->playback->playing == 0)
+        return;
+    g_free(output);
+}
+
+/**
+ * Decode all headers in the file and fill in stats
+ * @return FALSE if scan failed.
+ */
+gboolean scan_file(struct mad_info_t * info, gboolean fast)
+{
+    struct mad_stream stream;
+    struct mad_header header;
+    int remainder = 0;
+    int data_used = 0;
+    int len = 0;
+    int tagsize = 0;
+    unsigned char buffer[BUFFER_SIZE];
+    struct mad_frame frame;     /* to read xing data */
+    gboolean has_xing = FALSE;
+
+    mad_stream_init(&stream);
+    mad_header_init(&header);
+    mad_frame_init(&frame);
+    xing_init(&info->xing);
+
+    info->bitrate = 0;
+    info->pos = mad_timer_zero;
+
+#ifdef DEBUG
+    g_message("f: scan_file");
+#endif                          /* DEBUG */
+
+    while (1) {
+        remainder = stream.bufend - stream.next_frame;
+
+        /*
+           if (remainder >= BUFFER_SIZE)
+           {
+           printf("oh dear.. remainder = %d\n", remainder);
+           }
+         */
+
+        memcpy(buffer, stream.this_frame, remainder);
+        len = input_get_data(info, buffer + remainder,
+                             BUFFER_SIZE - remainder);
+
+        if (len <= 0)
+            break;
+
+        mad_stream_buffer(&stream, buffer, len + remainder);
+
+        while (1) {
+            if (mad_header_decode(&header, &stream) == -1) {
+                if (stream.error == MAD_ERROR_BUFLEN) {
+                    break;
+                }
+                if (!MAD_RECOVERABLE(stream.error)) {
+#ifdef DEBUG
+                    g_message("(fatal) error decoding header %d: %s",
+                              info->frames, mad_stream_errorstr(&stream));
+                    g_message("remainder = %d", remainder);
+                    g_message("len = %d", len);
+#endif                          /* DEBUG */
+                    break;
+                }
+                if (stream.error == MAD_ERROR_LOSTSYNC) {
+                    /* ignore LOSTSYNC due to ID3 tags */
+                    tagsize = id3_tag_query(stream.this_frame,
+                                            stream.bufend -
+                                            stream.this_frame);
+                    if (tagsize > 0) {
+#ifdef DEBUG
+                        g_message("skipping id3_tag: %d", tagsize);
+#endif                          /* DEBUG */
+                        mad_stream_skip(&stream, tagsize);
+                        continue;
+                    }
+                }
+#ifdef DEBUG
+                g_message("(recovered) error decoding header %d: %s",
+                          info->frames, mad_stream_errorstr(&stream));
+                g_message("remainder = %d", remainder);
+                g_message("len = %d", len);
+#endif                          /* DEBUG */
+                continue;
+            }
+            info->frames++;
+#ifdef DEBUG
+            g_message("duration = %lu",
+                      mad_timer_count(header.duration,
+                                      MAD_UNITS_MILLISECONDS));
+            g_message("size = %d", stream.next_frame - stream.this_frame);
+#endif
+            mad_timer_add(&info->duration, header.duration);
+            data_used += stream.next_frame - stream.this_frame;
+            if (info->frames == 1) {
+                /* most of these *should* remain constant */
+                info->bitrate = header.bitrate;
+                info->freq = header.samplerate;
+                info->channels = MAD_NCHANNELS(&header);
+                info->mpeg_layer = header.layer;
+                info->mode = header.mode;
+
+                if (audmad_config.use_xing) {
+                    frame.header = header;
+                    if (mad_frame_decode(&frame, &stream) == -1)
+                        break;
+                    if (xing_parse
+                        (&info->xing, stream.anc_ptr,
+                         stream.anc_bitlen) == 0) {
+#ifdef DEBUG
+                        g_message("found xing header");
+#endif                          /* DEBUG */
+                        has_xing = TRUE;
+                        info->vbr = TRUE;   /* otherwise xing header would have been 'Info' */
+                        info->frames = info->xing.frames;
+                        mad_timer_multiply(&info->duration, info->frames);
+                        info->bitrate =
+                            8.0 * info->xing.bytes /
+                            mad_timer_count(info->duration,
+                                            MAD_UNITS_SECONDS);
+                        break;
+                    }
+                }
+
+            }
+            else {
+                /* perhaps we have a VRB file */
+                if (info->bitrate != header.bitrate)
+                    info->vbr = TRUE;
+                if (info->vbr)
+                    info->bitrate += header.bitrate;
+                /* check for changin layer/samplerate/channels */
+                if (info->mpeg_layer != header.layer)
+                    g_warning("layer varies!!");
+                if (info->freq != header.samplerate)
+                    g_warning("samplerate varies!!");
+                if (info->channels != MAD_NCHANNELS(&header))
+                    g_warning("number of channels varies!!");
+            }
+
+            if (fast && info->frames >= N_AVERAGE_FRAMES) {
+                float frame_size = ((double) data_used) / N_AVERAGE_FRAMES;
+                info->frames = (info->size - tagsize) / frame_size;
+                //int frame_frac = (info->size - tagsize) % frame_size;
+                info->duration.seconds /= N_AVERAGE_FRAMES;
+                info->duration.fraction /= N_AVERAGE_FRAMES;
+                mad_timer_multiply(&info->duration, info->frames);
+#ifdef DEBUG
+                g_message("using fast playtime calculation");
+                g_message("data used = %d [tagsize=%d framesize=%f]",
+                          data_used, tagsize, frame_size);
+                //g_message ("frame_size = %f [frac=%d]", frame_size, frame_frac);
+                g_message("frames = %d, frequecy = %d, channels = %d",
+                          info->frames, info->freq, info->channels);
+                long millis = mad_timer_count(info->duration,
+                                              MAD_UNITS_MILLISECONDS);
+                g_message("duration = %lu:%lu", millis / 1000 / 60,
+                          (millis / 1000) % 60);
+#endif                          /* DEBUG */
+                break;
+            }
+        }
+        if (stream.error != MAD_ERROR_BUFLEN)
+            break;
+    }
+
+    if (info->vbr && !has_xing)
+        info->bitrate = info->bitrate / info->frames;
+
+    mad_frame_finish(&frame);
+    mad_header_finish(&header);
+    mad_stream_finish(&stream);
+    xing_finish(&info->xing);
+
+#ifdef DEBUG
+    g_message("e: scan_file");
+#endif                          /* DEBUG */
+    return (info->frames != 0 || info->remote == TRUE);
+}
+
+gpointer decode_loop(gpointer arg)
+{
+    unsigned char buffer[BUFFER_SIZE];
+    int len;
+    int seek_skip = 0;
+    int remainder = 0;
+    gint tlen;
+
+    /* mad structs */
+    struct mad_stream stream;
+    struct mad_frame frame;
+    struct mad_synth synth;
+
+    /* track info is passed in as thread argument */
+    struct mad_info_t *info = (struct mad_info_t *) arg;
+
+#ifdef DEBUG
+    g_message("f: decode");
+#endif                          /* DEBUG */
+
+    /* init mad stuff */
+    mad_frame_init(&frame);
+    mad_stream_init(&stream);
+    mad_synth_init(&synth);
+
+    if (!mad_plugin->output->
+        open_audio(info->fmt, info->freq, info->channels)) {
+        audmad_error("failed to open audio output: %s",
+                      mad_plugin->output->description);
+        g_message("failed to open audio output: %s",
+                  mad_plugin->output->description);
+        return NULL;
+    }
+
+    /* set mainwin title */
+    if (info->title)
+        g_free(info->title);
+    info->title =
+        xmms_get_titlestring(xmms_get_gentitle_format(), info->tuple);
+
+    tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS),
+    
+    mad_plugin->set_info(info->title,
+                        tlen == 0 ? -1 : tlen,
+                        info->bitrate, info->freq, info->channels);
+
+    /* main loop */
+    do {
+        if (info->playback->playing == 0) {
+#ifdef DEBUG
+            g_message("decode: stop signaled");
+#endif                          /* DEBUG */
+            break;
+        }
+        if (seek_skip)
+            remainder = 0;
+        else {
+            remainder = stream.bufend - stream.next_frame;
+            memcpy(buffer, stream.this_frame, remainder);
+        }
+        len = input_get_data(info, buffer + remainder,
+                             BUFFER_SIZE - remainder);
+        if (len <= 0) {
+#ifdef DEBUG
+            g_message("finished decoding");
+#endif                          /* DEBUG */
+            break;
+        }
+        len += remainder;
+        if (len < MAD_BUFFER_GUARD) {
+            int i;
+            for (i = len; i < MAD_BUFFER_GUARD; i++)
+                buffer[i] = 0;
+            len = MAD_BUFFER_GUARD;
+        }
+
+        mad_stream_buffer(&stream, buffer, len);
+
+        if (seek_skip) {
+#ifdef DEBUG
+            g_message("skipping: %d", seek_skip);
+#endif
+            int skip = 2;
+            do {
+                if (mad_frame_decode(&frame, &stream) == 0) {
+                    mad_timer_add(&info->pos, frame.header.duration);
+                    if (--skip == 0)
+                        mad_synth_frame(&synth, &frame);
+                }
+                else if (!MAD_RECOVERABLE(stream.error))
+                    break;
+            }
+            while (skip);
+            seek_skip = 0;
+        }
+
+        while (!info->playback->playing == 0) {
+            if (info->seek != -1 && !info->remote) {
+#ifdef DEBUG
+                g_message("seeking: %d", info->seek);
+#endif
+                int new_position;
+                int seconds =
+                    mad_timer_count(info->duration, MAD_UNITS_SECONDS);
+                if (info->seek >= seconds)
+                    info->seek = seconds;
+
+                mad_timer_set(&info->pos, info->seek, 0, 0);
+                new_position =
+                    ((double) info->seek / (double) seconds) * info->size;
+#ifdef DEBUG
+                g_message("seeking to: %d bytes", new_position);
+#endif
+                if (vfs_fseek(info->infile, new_position, SEEK_SET) == -1)
+                    audmad_error("failed to seek to: %d", new_position);
+                mad_frame_mute(&frame);
+                mad_synth_mute(&synth);
+                stream.error = MAD_ERROR_BUFLEN;
+                mad_plugin->output->flush(mad_timer_count
+                                         (info->pos,
+                                          MAD_UNITS_MILLISECONDS));
+                stream.sync = 0;
+                info->seek = -1;
+                seek_skip = 1;
+                break;
+            }
+
+            if (mad_header_decode(&frame.header, &stream) == -1) {
+                if (!MAD_RECOVERABLE(stream.error))
+                    break;
+                if (stream.error == MAD_ERROR_LOSTSYNC) {
+                    /* ignore LOSTSYNC due to ID3 tags */
+                    int tagsize = id3_tag_query(stream.this_frame,
+                                                stream.bufend -
+                                                stream.this_frame);
+                    if (tagsize > 0) {
+                        mad_stream_skip(&stream, tagsize);
+                        continue;
+                    }
+                }
+#ifdef DEBUG
+                g_message("(recovered) error decoding header %d: %s",
+                          info->current_frame,
+                          mad_stream_errorstr(&stream));
+#endif                          /* DEBUG */
+                continue;
+            }
+
+            if (mad_frame_decode(&frame, &stream) == -1) {
+                if (!MAD_RECOVERABLE(stream.error))
+                    break;
+#ifdef DEBUG
+                g_message("(recovered) error decoding frame %d: %s",
+                          info->current_frame,
+                          mad_stream_errorstr(&stream));
+#endif                          /* DEBUG */
+            }
+
+            info->current_frame++;
+
+            if (info->freq != frame.header.samplerate
+                || info->channels !=
+                (guint) MAD_NCHANNELS(&frame.header))
+	    {
+		tlen = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS);
+#ifdef DEBUG
+                g_message("re-opening audio due to change in audio type");
+                g_message("old: frequency = %d, channels = %d", info->freq,
+                          info->channels);
+                g_message("new: frequency = %d, channels = %d",
+                          frame.header.samplerate,
+                          (guint) MAD_NCHANNELS(&frame.header));
+#endif                          /* DEBUG */
+
+                info->freq = frame.header.samplerate;
+                info->channels = MAD_NCHANNELS(&frame.header);
+                mad_plugin->output->close_audio();
+                if (!mad_plugin->output->open_audio(info->fmt, info->freq,
+                                                   info->channels)) {
+                    audmad_error("failed to re-open audio output: %s",
+                                  mad_plugin->output->description);
+                }
+
+		mad_plugin->set_info(info->title,
+                        tlen != 0 ? tlen : -1,
+                        info->bitrate, info->freq, info->channels);
+            }
+            if (info->playback->playing == 0)
+                break;
+            mad_synth_frame(&synth, &frame);
+            mad_stream_sync(&stream);
+            write_output(info, &synth.pcm, &frame.header);
+            mad_timer_add(&info->pos, frame.header.duration);
+        }
+    }
+    while (stream.error == MAD_ERROR_BUFLEN);
+
+    /* free mad stuff */
+    mad_frame_finish(&frame);
+    mad_stream_finish(&stream);
+    mad_synth_finish(&synth);
+
+    if (!info->playback->playing == 0) {
+        mad_plugin->output->buffer_free();
+        mad_plugin->output->buffer_free();
+        while (mad_plugin->output->buffer_playing()) {
+#ifdef DEBUG
+            g_message("f: buffer_playing=%d",
+                      mad_plugin->output->buffer_playing());
+#endif
+            xmms_usleep(10000);
+            if (info->playback->playing == 0)
+                break;
+        }
+    }
+#ifdef DEBUG
+    g_message("e: decode");
+#endif                          /* DEBUG */
+
+    bmp_title_input_free(info->tuple);
+    info->tuple = NULL;
+
+    mad_plugin->output->close_audio();
+    info->playback->playing = 0;
+    g_thread_exit(0);
+    return NULL; /* dummy */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/dither.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,115 @@
+/* A C-program for MT19937: Integer     version                   */
+/*  genrand() generates one pseudorandom unsigned integer (32bit) */
+/* which is uniformly distributed among 0 to 2^32-1  for each     */
+/* call. sgenrand(seed) set initial values to the working area    */
+/* of 624 words. Before genrand(), sgenrand(seed) must be         */
+/* called once. (seed is any 32-bit integer except for 0).        */
+/*   Coded by Takuji Nishimura, considering the suggestions by    */
+/* Topher Cooper and Marc Rieffel in July-Aug. 1997.              */
+
+/* This library is free software; you can redistribute it and/or   */
+/* modify it under the terms of the GNU Library General Public     */
+/* License as published by the Free Software Foundation; either    */
+/* version 2 of the License, or (at your option) any later         */
+/* version.                                                        */
+/* This library is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of  */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.            */
+/* See the GNU Library General Public License for more details.    */
+/* You should have received a copy of the GNU Library General      */
+/* Public License along with this library; if not, write to the    */
+/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   */
+/* 02111-1307  USA                                                 */
+
+/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.       */
+/* Any feedback is very welcome. For any question, comments,       */
+/* see http://www.math.keio.ac.jp/matumoto/emt.html or email       */
+/* matumoto@math.keio.ac.jp                                        */
+
+#include <stdio.h>
+#include <assert.h>
+
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0df     /* constant vector a */
+#define UPPER_MASK 0x80000000   /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffff   /* least significant r bits */
+
+/* Tempering parameters */
+#define TEMPERING_MASK_B 0x9d2c5680
+#define TEMPERING_MASK_C 0xefc60000
+#define TEMPERING_SHIFT_U(y)  (y >> 11)
+#define TEMPERING_SHIFT_S(y)  (y << 7)
+#define TEMPERING_SHIFT_T(y)  (y << 15)
+#define TEMPERING_SHIFT_L(y)  (y >> 18)
+
+static unsigned long mt[N];     /* the array for the state vector  */
+static int mti = N + 1;         /* mti==N+1 means mt[N] is not initialized */
+
+/* initializing the array with a NONZERO seed */
+void sgenrand(seed)
+unsigned long seed;
+{
+    /* setting initial seeds to mt[N] using         */
+    /* the generator Line 25 of Table 1 in          */
+    /* [KNUTH 1981, The Art of Computer Programming */
+    /*    Vol. 2 (2nd Ed.), pp102]                  */
+    mt[0] = seed & 0xffffffff;
+    for (mti = 1; mti < N; mti++)
+        mt[mti] = (69069 * mt[mti - 1]) & 0xffffffff;
+}
+
+unsigned long genrand()
+{
+    unsigned long y;
+    static unsigned long mag01[2] = { 0x0, MATRIX_A };
+    /* mag01[x] = x * MATRIX_A  for x=0,1 */
+
+    if (mti >= N) {             /* generate N words at one time */
+        int kk;
+
+        if (mti == N + 1)       /* if sgenrand() has not been called, */
+            sgenrand(4357);     /* a default initial seed is used   */
+
+        for (kk = 0; kk < N - M; kk++) {
+            y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+            mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1];
+        }
+        for (; kk < N - 1; kk++) {
+            y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+            mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1];
+        }
+        y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
+        mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];
+
+        mti = 0;
+    }
+
+    y = mt[mti++];
+    y ^= TEMPERING_SHIFT_U(y);
+    y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
+    y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
+    y ^= TEMPERING_SHIFT_L(y);
+
+    return y;
+}
+
+long triangular_dither_noise(int nbits)
+{
+    // parameter nbits : the peak-to-peak amplitude desired (in bits)
+    //  use with nbits set to    2 + nber of bits to be trimmed.
+    // (because triangular is made from two uniformly distributed processes,
+    // it starts at 2 bits peak-to-peak amplitude)
+    // see The Theory of Dithered Quantization by Robert Alexander Wannamaker
+    // for complete proof of why that's optimal
+
+    long v = (genrand() / 2 - genrand() / 2);   // in ]-2^31, 2^31[
+    //int signe = (v>0) ? 1 : -1;
+    long P = 1 << (32 - nbits); // the power of 2
+    v /= P;
+    // now v in ]-2^(nbits-1), 2^(nbits-1) [ 
+
+    return v;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/fileinfo.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,644 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include "plugin.h"
+#include "input.h"
+
+#include "mp3.xpm"
+
+#include <math.h>
+#include <audacious/util.h>
+#include <gtk/gtk.h>
+
+/* yaz */
+#include <langinfo.h>
+
+#ifndef NOGUI
+static GtkWidget *window = 0;
+static GtkWidget *filename_entry, *id3_frame;
+static GtkWidget *title_entry, *artist_entry, *album_entry;
+static GtkWidget *year_entry, *tracknum_entry, *comment_entry;
+static GtkWidget *genre_combo;
+static GtkWidget *mpeg_level, *mpeg_bitrate, *mpeg_samplerate,
+    *mpeg_frames, *mpeg_duration, *mpeg_flags;
+static GtkWidget *mpeg_fileinfo, *mpeg_replaygain, *mpeg_replaygain2,
+    *mpeg_replaygain3, *mpeg_replaygain4, *mp3gain1, *mp3gain2;
+#endif                          /* !NOGUI */
+
+static GList *genre_list = 0;
+static struct mad_info_t info;
+struct id3_frame *id3_frame_new(const char *str);
+
+#ifndef NOGUI
+static void
+update_id3_frame(struct id3_tag *tag, const char *frame_name,
+                 const char *data)
+{
+    int res;
+    struct id3_frame *frame;
+    union id3_field *field;
+    id3_ucs4_t *ucs4;
+
+    if (data == NULL)
+        return;
+
+    /*
+     * An empty string removes the frame altogether.
+     */
+    if (strlen(data) == 0) {
+        while ((frame = id3_tag_findframe(tag, frame_name, 0)))
+            id3_tag_detachframe(tag, frame);
+        return;
+    }
+
+    frame = id3_tag_findframe(tag, frame_name, 0);
+    if (!frame) {
+        frame = id3_frame_new(frame_name);
+        id3_tag_attachframe(tag, frame);
+    }
+
+    if (frame_name == ID3_FRAME_COMMENT) {
+        field = id3_frame_field(frame, 3);
+        field->type = ID3_FIELD_TYPE_STRINGFULL;
+    }
+    else {
+        field = id3_frame_field(frame, 1);
+        field->type = ID3_FIELD_TYPE_STRINGLIST;
+    }
+
+    id3_field_settextencoding(field, ID3_FIELD_TEXTENCODING_UTF_8);
+
+    ucs4 = id3_utf8_ucs4duplicate((const unsigned char *) data);
+
+    if (frame_name == ID3_FRAME_GENRE) {
+        char *tmp;
+        int index = id3_genre_number(ucs4);
+        g_free(ucs4);
+        tmp = g_strdup_printf("%d", index);
+        ucs4 = id3_utf8_ucs4duplicate((unsigned char *) tmp);
+    }
+
+    if (frame_name == ID3_FRAME_COMMENT) {
+        res = id3_field_setfullstring(field, ucs4);
+    }
+    else {
+        res = id3_field_setstrings(field, 1, &ucs4);
+    }
+
+    if (res != 0)
+        g_print("error setting id3 field: %s\n", frame_name);
+}
+
+static void close_window(GtkWidget * w, gpointer data)
+{
+    input_term(&info);
+    gtk_widget_destroy(window);
+}
+
+static void save_cb(GtkWidget * w, gpointer data)
+{
+    gchar *text;
+    struct id3_file *id3file;
+    struct id3_tag *id3tag;
+    char *codeset;
+
+    if (info.remote)
+        return;
+
+    /* read tag from file */
+    id3file = id3_file_open(info.filename, ID3_FILE_MODE_READWRITE);
+    if (!id3file) {
+        id3tag = id3_tag_new();
+        id3_tag_clearframes(id3tag);
+        if (!id3file) {
+            xmms_show_message("File Info", "Couldn't open file!", "Ok",
+                              FALSE, NULL, NULL);
+            return;
+        }
+    }
+
+    id3tag = id3_file_tag(id3file);
+    if (!id3tag) {
+        id3tag = id3_tag_new();
+    }
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(title_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_TITLE, text);
+    free(text);
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(artist_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_ARTIST, text);
+    free(text);
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(album_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_ALBUM, text);
+    free(text);
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(year_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_YEAR, text);
+    free(text);
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(comment_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_COMMENT, text);
+    free(text);
+
+    text = gtk_editable_get_chars(GTK_EDITABLE(tracknum_entry), 0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_TRACK, text);
+    free(text);
+
+    text =
+        gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(genre_combo)->entry),
+                               0, -1);
+    update_id3_frame(id3tag, ID3_FRAME_GENRE, text);
+    free(text);
+
+    if (id3_file_update(id3file) != 0) {
+        xmms_show_message("File Info", "Couldn't write tag!", "Ok", FALSE,
+                          NULL, NULL);
+    }
+    id3_file_close(id3file);
+}
+
+static void remove_id3_cb(GtkWidget * w, gpointer data)
+{
+    struct id3_file *id3file;
+    struct id3_tag *id3tag;
+
+    id3file = id3_file_open(info.filename, ID3_FILE_MODE_READWRITE);
+
+    if (id3file == NULL)
+        return;
+
+    id3tag = id3_file_tag(id3file);
+
+    if (id3tag == NULL)
+    {
+        id3_file_close(id3file);
+        return;
+    }
+
+    id3_tag_clearframes(id3tag);
+    id3_file_update(id3file);
+    id3_file_close(id3file);
+
+    gtk_entry_set_text(GTK_ENTRY(title_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(artist_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(album_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(comment_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(year_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(tracknum_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), "");
+    gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+    gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
+}
+
+static void
+change_buttons(GtkWidget * object)
+{
+    gtk_widget_set_sensitive(GTK_WIDGET(object), TRUE);
+}
+
+void create_window()
+{
+    GtkWidget *vbox, *hbox, *left_vbox, *table;
+    GtkWidget *mpeg_frame, *mpeg_box;
+    GtkWidget *label, *filename_hbox;
+    GtkWidget *bbox, *save, *remove_id3, *cancel;
+    GtkWidget *pixmapwid;
+    GdkPixbuf *pixbuf;
+
+    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_type_hint(GTK_WINDOW(window),
+			     GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+    g_signal_connect(G_OBJECT(window), "destroy",
+                       G_CALLBACK(close_window), &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);
+
+    pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **)
+                                          gnome_mime_audio_xpm);
+    pixmapwid = gtk_image_new_from_pixbuf(pixbuf);
+    g_object_unref(pixbuf);
+    gtk_misc_set_alignment(GTK_MISC(pixmapwid), 0, 0);
+    gtk_box_pack_start(GTK_BOX(filename_hbox), pixmapwid, FALSE, FALSE,
+                       0);
+
+    label = gtk_label_new("<b>Name:</b>");
+    gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+    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_table_new(2, 4, FALSE);
+    gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0);
+
+    mpeg_frame = gtk_frame_new(_(" MPEG Info "));
+    gtk_table_attach(GTK_TABLE(left_vbox), mpeg_frame, 0, 2, 0, 1,
+	GTK_FILL, GTK_FILL, 5, 5);
+
+    mpeg_box = gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_box);
+    gtk_container_set_border_width(GTK_CONTAINER(mpeg_box), 10);
+    gtk_box_set_spacing(GTK_BOX(mpeg_box), 0);
+
+    mpeg_level = gtk_label_new("");
+    gtk_widget_set_usize(mpeg_level, 120, -2);
+    gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_level, FALSE, FALSE, 0);
+
+    mpeg_bitrate = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_bitrate), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_bitrate, FALSE, FALSE, 0);
+
+    mpeg_samplerate = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_samplerate, FALSE, FALSE,
+                       0);
+
+    mpeg_flags = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_flags, FALSE, FALSE, 0);
+
+    mpeg_frames = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_frames), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_frames), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_frames, FALSE, FALSE, 0);
+
+    mpeg_duration = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_duration), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_duration), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_duration, FALSE, FALSE, 0);
+
+    mpeg_replaygain = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_replaygain), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain, FALSE, FALSE,
+                       0);
+    mpeg_replaygain2 = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain2), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_replaygain2), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain2, FALSE, FALSE,
+                       0);
+    mpeg_replaygain3 = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain3), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_replaygain3), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain3, FALSE, FALSE,
+                       0);
+    mpeg_replaygain4 = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain4), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_replaygain4), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain4, FALSE, FALSE,
+                       0);
+    mp3gain1 = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mp3gain1), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mp3gain1), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mp3gain1, FALSE, FALSE, 0);
+    mp3gain2 = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mp3gain2), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mp3gain2), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mp3gain2, FALSE, FALSE, 0);
+
+    mpeg_fileinfo = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0);
+    gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT);
+    gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, FALSE, FALSE, 0);
+
+    id3_frame = gtk_frame_new(_(" ID3 Tag "));
+    gtk_table_attach(GTK_TABLE(left_vbox), id3_frame, 2, 4, 0, 1,
+	GTK_FILL, GTK_FILL, 0, 5);
+
+    table = gtk_table_new(5, 5, FALSE);
+    gtk_container_set_border_width(GTK_CONTAINER(table), 5);
+    gtk_container_add(GTK_CONTAINER(id3_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("Year:");
+    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);
+
+    year_entry = gtk_entry_new();
+    gtk_widget_set_usize(year_entry, 40, -1);
+    gtk_table_attach(GTK_TABLE(table), year_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),
+                           FALSE);
+    if (!genre_list) {
+        int i = 0;
+        const id3_ucs4_t *ucs4 = id3_genre_index(i);
+        while (ucs4) {
+            genre_list =
+                g_list_append(genre_list, id3_ucs4_utf8duplicate(ucs4));
+            i++;
+            ucs4 = id3_genre_index(i);
+        }
+    }
+    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_hbutton_box_set_layout_default(GTK_BUTTONBOX_SPREAD);
+
+    save = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+    g_signal_connect(G_OBJECT(save), "clicked",
+                       G_CALLBACK(save_cb), NULL);
+    gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0);
+    gtk_widget_grab_default(save);
+
+    remove_id3 = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+    g_signal_connect(G_OBJECT(remove_id3), "clicked",
+                       G_CALLBACK(remove_id3_cb), save);
+    gtk_box_pack_start(GTK_BOX(bbox), remove_id3, TRUE, TRUE, 0);
+
+    g_signal_connect_swapped(G_OBJECT(title_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+    g_signal_connect_swapped(G_OBJECT(artist_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+    g_signal_connect_swapped(G_OBJECT(album_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+    g_signal_connect_swapped(G_OBJECT(year_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+    g_signal_connect_swapped(G_OBJECT(comment_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+    g_signal_connect_swapped(G_OBJECT(tracknum_entry), "changed",
+                             G_CALLBACK(change_buttons), save);
+        
+    g_signal_connect_swapped(G_OBJECT(GTK_COMBO(genre_combo)->entry), "changed",
+                                 G_CALLBACK(change_buttons), save);
+
+
+    gtk_table_attach(GTK_TABLE(table), bbox, 0, 5, 6, 7, GTK_FILL, 0,
+                     0, 8);
+
+    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_table_attach(GTK_TABLE(left_vbox), bbox, 0, 4, 1, 2, GTK_FILL,
+                         0, 0, 8);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    g_signal_connect_swapped(G_OBJECT(cancel), "clicked",
+                             G_CALLBACK(gtk_widget_destroy),
+                             G_OBJECT(window));
+    GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+    gtk_widget_show_all(window);
+}
+
+static void id3_frame_to_entry(char *framename, GtkEntry * entry)
+{
+    gtk_entry_set_text(entry, "");
+
+    if (info.tag) {
+        gchar *text = input_id3_get_string(info.tag, framename);
+        if (text) {
+            gtk_entry_set_text(entry, text);
+            g_free(text);
+        }
+    }
+}
+#endif                          /* !NOGUI */
+
+void audmad_get_file_info(char *filename)
+{
+#ifndef NOGUI
+    gchar *title;
+    gchar message[128];
+    static char const *const layer_str[3] = { "MPEG-1 Layer I", "MPEG-1 Layer II", "MPEG-1 Layer III" };
+    static char const *const mode_str[4] = {
+        ("single channel"), ("dual channel"), "joint stereo", "stereo"
+    };
+    char *utf_filename;
+
+#ifdef DEBUG
+    g_message("f: audmad_get_file_info: %s\n", filename);
+#endif
+    utf_filename = str_to_utf8(filename);
+    create_window();
+
+    input_init(&info, filename);
+    input_get_info(&info, FALSE);
+
+    title = g_strdup_printf("%s - Audacious", g_basename(filename));
+    gtk_window_set_title(GTK_WINDOW(window), title);
+    g_free(title);
+
+    gtk_entry_set_text(GTK_ENTRY(filename_entry), utf_filename);
+    gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1);
+
+    free(utf_filename);
+
+    id3_frame_to_entry(ID3_FRAME_ARTIST, GTK_ENTRY(artist_entry));
+    id3_frame_to_entry(ID3_FRAME_TITLE, GTK_ENTRY(title_entry));
+    id3_frame_to_entry(ID3_FRAME_ALBUM, GTK_ENTRY(album_entry));
+
+// year
+// id3_frame_to_entry (ID3_FRAME_YEAR, GTK_ENTRY (year_entry));
+// to set year entry, we have to do manually because TYER is still used equally to TDRC.
+    gtk_entry_set_text(GTK_ENTRY(year_entry), "");
+    if (info.tag) {
+        gchar *text = NULL;
+        text = input_id3_get_string(info.tag, "TDRC");
+        if (!text)
+            text = input_id3_get_string(info.tag, "TYER");
+        if (text) {
+            gtk_entry_set_text(GTK_ENTRY(year_entry), text);
+            g_free(text);
+        }
+    }
+
+    id3_frame_to_entry(ID3_FRAME_TRACK, GTK_ENTRY(tracknum_entry));
+    id3_frame_to_entry(ID3_FRAME_COMMENT, GTK_ENTRY(comment_entry));
+    snprintf(message, 127, "%s", layer_str[info.mpeg_layer - 1]);
+    gtk_label_set_text(GTK_LABEL(mpeg_level), message);
+    if (info.vbr) {
+        snprintf(message, 127, "VBR (avg. %d kbps)", info.bitrate / 1000);
+    }
+    else {
+        snprintf(message, 127, "%d KBit/s", info.bitrate / 1000);
+    }
+    gtk_label_set_text(GTK_LABEL(mpeg_bitrate), message);
+    snprintf(message, 127, "%d Hz", info.freq);
+    gtk_label_set_text(GTK_LABEL(mpeg_samplerate), message);
+    if (info.frames != -1) {
+        snprintf(message, 127, "%d frames", info.frames);
+        gtk_label_set_text(GTK_LABEL(mpeg_frames), message);
+    }
+    else {
+        gtk_label_set_text(GTK_LABEL(mpeg_frames), "");
+    }
+    gtk_label_set_text(GTK_LABEL(mpeg_flags), mode_str[info.mode]);
+    snprintf(message, 127, "%ld  seconds",
+             mad_timer_count(info.duration, MAD_UNITS_SECONDS));
+    gtk_label_set_text(GTK_LABEL(mpeg_duration), message);
+
+    if (info.replaygain_album_str != NULL) {
+        snprintf(message, 127, "RG_album=%4s (x%4.2f)",
+                 info.replaygain_album_str, info.replaygain_album_scale);
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain), "");
+
+    if (info.replaygain_track_str != NULL) {
+        snprintf(message, 127, "RG_track=%4s (x%4.2f)",
+                 info.replaygain_track_str, info.replaygain_track_scale);
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain2), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain2), "");
+
+    if (info.replaygain_album_peak_str != NULL) {
+        snprintf(message, 127, "Peak album=%4s (%+5.3fdBFS)",
+                 info.replaygain_album_peak_str,
+                 20 * log10(info.replaygain_album_peak));
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), "");
+
+    if (info.replaygain_track_peak_str != NULL) {
+        snprintf(message, 127, "Peak track=%4s (%+5.3fdBFS)",
+                 info.replaygain_track_peak_str,
+                 20 * log10(info.replaygain_track_peak));
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain4), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), "");
+
+    if (info.mp3gain_undo_str != NULL) {
+        snprintf(message, 127, "mp3gain undo=%4s (%+5.3fdB)",
+                 info.mp3gain_undo_str, info.mp3gain_undo);
+        gtk_label_set_text(GTK_LABEL(mp3gain1), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mp3gain1), "");
+
+    if (info.mp3gain_minmax_str != NULL) {
+        snprintf(message, 127, "mp3gain minmax=%4s (max-min=%+6.3fdB)",
+                 info.mp3gain_minmax_str, info.mp3gain_minmax);
+        gtk_label_set_text(GTK_LABEL(mp3gain2), message);
+    }
+    else
+        gtk_label_set_text(GTK_LABEL(mp3gain2), "");
+
+    gtk_label_set_text(GTK_LABEL(mpeg_fileinfo), "");
+
+
+    /* work out the index of the genre in the list */
+    {
+        const id3_ucs4_t *string;
+        struct id3_frame *frame;
+        union id3_field *field;
+        frame = id3_tag_findframe(info.tag, ID3_FRAME_GENRE, 0);
+        if (frame) {
+            field = id3_frame_field(frame, 1);
+            string = id3_field_getstrings(field, 0);
+            string = id3_genre_name(string);
+            if (string)
+                gtk_list_select_item(GTK_LIST
+                                     (GTK_COMBO(genre_combo)->list),
+                                     id3_genre_number(string));
+        }
+    }
+
+    gtk_widget_set_sensitive(id3_frame, TRUE);
+
+#endif                          /* !NOGUI */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/input.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,518 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif                          /* HAVE_ASSERT_H */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif                          /* HAVE_SYS_TYPES_H */
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif                          /* HAVE_SYS_SOCKET_H */
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif                          /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif                          /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif                          /* HAVE_NETDB_H */
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif                          /* HAVE_SYS_STAT_H */
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif                          /* HAVE_SYS_TIME_H */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <audacious/util.h>
+
+#include "input.h"
+#include "replaygain.h"
+
+#define DIR_SEPARATOR '/'
+#define HEADER_SIZE 256
+#define LINE_LENGTH 256
+
+extern gboolean scan_file(struct mad_info_t *info, gboolean fast);
+
+/**
+ * init the mad_info_t struct.
+ */
+gboolean input_init(struct mad_info_t * info, const char *url)
+{
+#ifdef DEBUG
+    g_message("f: input_init");
+#endif
+    memset(info, 0, sizeof(struct mad_info_t));
+
+    info->fmt = FMT_S16_LE;
+    info->channels = -1;
+    info->mpeg_layer = -1;
+    info->size = -1;
+    info->freq = -1;
+    info->seek = -1;
+    info->duration = mad_timer_zero;
+    info->pos = mad_timer_zero;
+    info->url = g_strdup(url);
+    info->current_frame = 0;
+    info->frames = 0;
+    info->bitrate = 0;
+    info->vbr = 0;
+    info->mode = 0;
+    info->title = 0;
+    info->offset = 0;
+
+    info->replaygain_album_str = 0;
+    info->replaygain_track_str = 0;
+    info->replaygain_album_peak_str = 0;
+    info->replaygain_track_peak_str = 0;
+    info->mp3gain_undo_str = 0;
+    info->mp3gain_minmax_str = 0;
+
+    info->tuple = NULL;
+    info->filename = g_strdup(url);
+
+    info->infile = vfs_fopen(info->filename, "rb");
+    if (info->infile == NULL)
+        return FALSE;
+
+    // obtain file size
+    vfs_fseek(info->infile, 0, SEEK_END);
+    info->size = vfs_ftell(info->infile);
+    vfs_fseek(info->infile, 0, SEEK_SET);
+    info->remote = info->size == 0 ? TRUE : FALSE;
+
+#ifdef DEBUG
+    g_message("i: info->size == %lu", info->size);
+    g_message("e: input_init");
+#endif
+    return TRUE;
+}
+
+/* return length in letters */
+size_t mad_ucs4len(id3_ucs4_t *ucs)
+{
+    id3_ucs4_t *ptr = ucs;
+    size_t len = 0;
+
+    while(*ptr++ != 0)
+        len++;
+
+    return len;
+}
+
+/* duplicate id3_ucs4_t string. new string will be terminated with 0. */
+id3_ucs4_t *mad_ucs4dup(id3_ucs4_t *org)
+{
+    id3_ucs4_t *new = NULL;
+    size_t len = mad_ucs4len(org);
+
+    new = g_malloc0((len + 1) * sizeof(id3_ucs4_t));
+    memcpy(new, org, len * sizeof(id3_ucs4_t));
+    *(new + len) = 0; //terminate
+
+    return new;
+}
+
+#define BYTES(x) ((x) * sizeof(id3_ucs4_t))
+
+id3_ucs4_t *mad_parse_genre(const id3_ucs4_t *string)
+{
+    id3_ucs4_t *ret = NULL;
+    id3_ucs4_t *tmp = NULL;
+    id3_ucs4_t *genre = NULL;
+    id3_ucs4_t *ptr, *end, *tail, *tp;
+    size_t ret_len = 0; //num of ucs4 char!
+    size_t tmp_len = 0;
+    gboolean is_num = TRUE;
+
+    tail = (id3_ucs4_t *)string + mad_ucs4len((id3_ucs4_t *)string);
+
+    ret = g_malloc0(1024); // realloc() is too picky
+
+    for(ptr = (id3_ucs4_t *)string; *ptr != 0 && ptr <= tail; ptr++) {
+        if(*ptr == '(') {
+            if(*(++ptr) == '(') { // escaped text like: ((something)
+                for(end = ptr; *end != ')' && *end != 0;) { // copy "(something)"
+                    end++;
+                }
+                end++; //include trailing ')'
+//                ret = g_realloc(ret, BYTES(end - ptr + 1));
+                memcpy(ret, ptr, BYTES(end - ptr));
+                ret_len += (end - ptr);
+                *(ret + ret_len) = 0; //terminate
+                ptr = end + 1;
+            }
+            else {
+                // reference to an id3v1 genre code
+                for(end = ptr; *end != ')' && *end != 0;) {
+                    end++;
+                }
+
+                tmp = g_malloc0(BYTES(end - ptr + 1));
+                memcpy(tmp, ptr, BYTES(end - ptr));
+                *(tmp + (end - ptr)) = 0; //terminate
+                ptr += end - ptr;
+
+                genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp);
+
+                g_free(tmp);
+                tmp = NULL;
+
+                tmp_len = mad_ucs4len(genre);
+
+//                ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1));
+                memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len));
+
+                ret_len += tmp_len;
+                *(ret + ret_len) = 0; //terminate
+            }
+        }
+        else {
+            for(end = ptr; *end != '(' && *end != 0; ) {
+                end++;
+            }
+            // scan string to determine whether a genre code number or not
+            tp = ptr;
+            is_num = TRUE;
+            while(tp < end) {
+                if(*tp < '0' || *tp > '9') { // anything else than number appears.
+                    is_num = FALSE;
+                    break;
+                }
+                tp++;
+            }
+            if(is_num) {
+#ifdef DEBUG
+                printf("is_num!\n");
+#endif
+                tmp = g_malloc0(BYTES(end - ptr + 1));
+                memcpy(tmp, ptr, BYTES(end - ptr));
+                *(tmp + (end - ptr)) = 0; //terminate
+                ptr += end - ptr;
+
+                genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp);
+#ifdef DEBUG
+                printf("genre length = %d\n", mad_ucs4len(genre));
+#endif
+                g_free(tmp);
+                tmp = NULL;
+
+                tmp_len = mad_ucs4len(genre);
+
+//                ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1));
+                memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len));
+
+                ret_len += tmp_len;
+                *(ret + ret_len) = 0; //terminate
+            }
+            else { // plain text
+//                ret = g_realloc(ret, BYTES(end - ptr + 1));
+#ifdef DEBUG
+                printf("plain!\n");
+                printf("ret_len = %d\n", ret_len);
+#endif
+                memcpy(ret + BYTES(ret_len), ptr, BYTES(end - ptr));
+                ret_len = ret_len + (end - ptr);
+                *(ret + ret_len) = 0; //terminate
+                ptr += (end - ptr);
+            }
+        }
+    }
+    return ret;
+}
+
+
+gchar *input_id3_get_string(struct id3_tag * tag, char *frame_name)
+{
+    gchar *rtn;
+    const id3_ucs4_t *string_const;
+    id3_ucs4_t *string;
+    struct id3_frame *frame;
+    union id3_field *field;
+
+    frame = id3_tag_findframe(tag, frame_name, 0);
+    if (!frame)
+        return NULL;
+
+    if (frame_name == ID3_FRAME_COMMENT)
+        field = id3_frame_field(frame, 3);
+    else
+        field = id3_frame_field(frame, 1);
+
+    if (!field)
+        return NULL;
+
+    if (frame_name == ID3_FRAME_COMMENT)
+        string_const = id3_field_getfullstring(field);
+    else
+        string_const = id3_field_getstrings(field, 0);
+
+    if (!string_const)
+        return NULL;
+
+    string = mad_ucs4dup((id3_ucs4_t *)string_const);
+
+    if (frame_name == ID3_FRAME_GENRE) {
+        id3_ucs4_t *string2 = NULL;
+        string2 = mad_parse_genre(string);
+        g_free((void *)string);
+        string = string2;
+    }
+
+    {
+        id3_utf8_t *string2 = id3_ucs4_utf8duplicate(string);
+        rtn = str_to_utf8(string2);
+        g_free(string2);
+    }
+
+#ifdef DEBUG
+    g_print("i: string = %s\n", rtn);
+#endif
+    return rtn;
+}
+
+/**
+ * read the ID3 tag 
+ */
+static void input_read_tag(struct mad_info_t *info)
+{
+    gchar *string = NULL;
+    TitleInput *title_input;
+
+    if (info->tuple == NULL) {
+        title_input = bmp_title_input_new();
+        info->tuple = title_input;
+    }
+    else
+        title_input = info->tuple;
+
+    info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY);
+    if (!info->id3file) {
+        return;
+    }
+
+    info->tag = id3_file_tag(info->id3file);
+    if (!info->tag) {
+        return;
+    }
+
+    title_input->performer =
+        input_id3_get_string(info->tag, ID3_FRAME_ARTIST);
+    title_input->track_name =
+        input_id3_get_string(info->tag, ID3_FRAME_TITLE);
+    title_input->album_name =
+        input_id3_get_string(info->tag, ID3_FRAME_ALBUM);
+    title_input->genre = input_id3_get_string(info->tag, ID3_FRAME_GENRE);
+    title_input->comment =
+        input_id3_get_string(info->tag, ID3_FRAME_COMMENT);
+    string = input_id3_get_string(info->tag, ID3_FRAME_TRACK);
+    if (string) {
+        title_input->track_number = atoi(string);
+        g_free(string);
+        string = NULL;
+    }
+    // year
+    string = NULL;
+    string = input_id3_get_string(info->tag, ID3_FRAME_YEAR);   //TDRC
+    if (!string)
+        string = input_id3_get_string(info->tag, "TYER");
+
+    if (string) {
+        title_input->year = atoi(string);
+        g_free(string);
+        string = NULL;
+    }
+
+    title_input->file_name = g_strdup(g_basename(info->filename));
+    title_input->file_path = g_path_get_dirname(info->filename);
+    if ((string = strrchr(title_input->file_name, '.'))) {
+        title_input->file_ext = string + 1;
+        *string = '\0';         // make filename end at dot.
+    }
+
+    info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ?
+        audmad_config.id3_format : xmms_get_gentitle_format(), title_input);
+}
+
+/**
+ * Retrieve meta-information about URL.
+ * For local files this means ID3 tag etc.
+ */
+gboolean input_get_info(struct mad_info_t *info, gboolean fast_scan)
+{
+#ifdef DEBUG
+    g_message("f: input_get_info: %s", info->title);
+#endif                          /* DEBUG */
+    input_read_tag(info);
+    input_read_replaygain(info);
+
+    /* scan mp3 file, decoding headers unless fast_scan is set */
+    if (scan_file(info, fast_scan) == FALSE)
+        return FALSE;
+
+    /* reset the input file to the start */
+    vfs_rewind(info->infile);
+    info->offset = 0;
+
+    if (info->remote)
+    {
+        gchar *stream_name = vfs_get_metadata(info->infile, "stream-name");
+        gchar *track_name = vfs_get_metadata(info->infile, "track-name");
+        gchar *tmp = NULL;
+
+        g_free(info->title);
+        g_free(info->tuple->track_name);
+        g_free(info->tuple->album_name);
+
+        info->title = g_strdup(track_name);
+        info->tuple->track_name = g_strdup(track_name);
+        info->tuple->album_name = g_strdup(stream_name);
+        tmp = g_strdup_printf("%s (%s)", track_name, stream_name);
+        mad_plugin->set_info(tmp,
+                             -1, // indicates the stream is unseekable
+                             info->bitrate, info->freq, info->channels);
+        g_free(tmp); g_free(stream_name); g_free(track_name);
+    }
+
+    /* use the filename for the title as a last resort */
+    if (!info->title)
+    {
+        char *pos = strrchr(info->filename, DIR_SEPARATOR);
+        if (pos)
+            info->title = g_strdup(pos + 1);
+        else
+            info->title = g_strdup(info->filename);
+    }
+
+#ifdef DEBUG
+    g_message("e: input_get_info");
+#endif                          /* DEBUG */
+    return TRUE;
+}
+
+
+/**
+ * Read data from the source given my madinfo into the buffer
+ * provided.  Return the number of bytes read.
+ * @return 0 on EOF
+ * @return -1 on error
+ */
+int
+input_get_data(struct mad_info_t *madinfo, guchar * buffer,
+               int buffer_size)
+{
+    int len = 0;
+#ifdef DEBUG
+//  g_message ("f: input_get_data: %d", buffer_size);
+#endif
+
+    /* simply read to data from the file */
+    len = vfs_fread(buffer, 1, buffer_size, madinfo->infile);
+
+    if (len == 0 && madinfo->playback)
+        madinfo->playback->eof = TRUE;
+
+    if (madinfo->remote)
+    {
+        gchar *stream_name = vfs_get_metadata(madinfo->infile, "stream-name");
+        gchar *track_name = vfs_get_metadata(madinfo->infile, "track-name");
+        gchar *tmp = NULL;
+
+        g_free(madinfo->title);
+        g_free(madinfo->tuple->track_name);
+        g_free(madinfo->tuple->album_name);
+
+        madinfo->title = g_strdup(track_name);
+        madinfo->tuple->track_name = g_strdup(track_name);
+        madinfo->tuple->album_name = g_strdup(stream_name);
+        tmp = g_strdup_printf("%s (%s)", track_name, stream_name);
+        mad_plugin->set_info(tmp,
+                             -1, // indicates the stream is unseekable
+                             madinfo->bitrate, madinfo->freq, madinfo->channels);
+        g_free(tmp); g_free(stream_name); g_free(track_name);
+    }
+
+#ifdef DEBUG
+//  g_message ("e: input_get_data: size=%d offset=%d", len, madinfo->offset);
+#endif
+    madinfo->offset += len;
+    return len;
+}
+
+/**
+ * Free up all mad_info_t related resourses.
+ */
+gboolean input_term(struct mad_info_t * info)
+{
+#ifdef DEBUG
+    g_message("f: input_term");
+#endif
+
+    if (info->title)
+        g_free(info->title);
+    if (info->url)
+        g_free(info->url);
+    if (info->filename)
+        g_free(info->filename);
+    if (info->infile)
+        vfs_fclose(info->infile);
+    if (info->id3file)
+        id3_file_close(info->id3file);
+
+    if (info->replaygain_album_str)
+        g_free(info->replaygain_album_str);
+    if (info->replaygain_track_str)
+        g_free(info->replaygain_track_str);
+    if (info->replaygain_album_peak_str)
+        g_free(info->replaygain_album_peak_str);
+    if (info->replaygain_track_peak_str)
+        g_free(info->replaygain_track_peak_str);
+    if (info->mp3gain_undo_str)
+        g_free(info->mp3gain_undo_str);
+    if (info->mp3gain_minmax_str)
+        g_free(info->mp3gain_minmax_str);
+
+    if (info->tuple) {
+        bmp_title_input_free(info->tuple);
+        info->tuple = NULL;
+    }
+
+    /* set everything to zero in case it gets used again. */
+    memset(info, 0, sizeof(struct mad_info_t));
+#ifdef DEBUG
+    g_message("e: input_term");
+#endif
+    return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/input.h	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,35 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include "plugin.h"
+
+gboolean input_init(struct mad_info_t *songinfo, const gchar * url);
+gboolean input_term(struct mad_info_t *songinfo);
+gboolean input_get_info(struct mad_info_t *songinfo, gboolean fast_scan);
+gint input_get_data(struct mad_info_t *songinfo, guchar * buffer,
+                    gint buffer_size);
+gint input_udp_read();
+gchar *input_id3_get_string(struct id3_tag *tag, char *frame_name);
+
+#endif                          /* ! INPUT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/mp3.xpm	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,963 @@
+/* XPM */
+static char * gnome_mime_audio_xpm[] = {
+"48 52 908 2",
+"  	c None",
+". 	c #000000",
+"+ 	c #010100",
+"@ 	c #020201",
+"# 	c #030201",
+"$ 	c #232323",
+"% 	c #1F1F1F",
+"& 	c #DADADA",
+"* 	c #FFFFFF",
+"= 	c #F6F6F6",
+"- 	c #CFCFCF",
+"; 	c #707070",
+"> 	c #FFFFFE",
+", 	c #FEFEFE",
+"' 	c #FBFBFB",
+") 	c #EDEDED",
+"! 	c #C0C0C0",
+"~ 	c #FFFEFE",
+"{ 	c #F8F5F2",
+"] 	c #EBE7E1",
+"^ 	c #ECEDE8",
+"/ 	c #ECEFEA",
+"( 	c #E6E9E4",
+"_ 	c #CED0CE",
+": 	c #BCBFBC",
+"< 	c #F0F0EF",
+"[ 	c #FEFEFD",
+"} 	c #F7F7F7",
+"| 	c #D7D7D7",
+"1 	c #F1F1F1",
+"2 	c #B7B7B7",
+"3 	c #EAEDEA",
+"4 	c #8EA299",
+"5 	c #697D70",
+"6 	c #546459",
+"7 	c #404D44",
+"8 	c #2C352F",
+"9 	c #4E5649",
+"0 	c #D6D8D3",
+"a 	c #D2D2D2",
+"b 	c #AEAEAE",
+"c 	c #070707",
+"d 	c #FCFCFB",
+"e 	c #6D7C75",
+"f 	c #5A5D4C",
+"g 	c #6B735F",
+"h 	c #6E7B63",
+"i 	c #5B6052",
+"j 	c #4A5042",
+"k 	c #CED0CB",
+"l 	c #FEFDFD",
+"m 	c #FDFDFD",
+"n 	c #B8B8B8",
+"o 	c #DCDCDC",
+"p 	c #A4A4A3",
+"q 	c #0E0E0E",
+"r 	c #8A928F",
+"s 	c #6A7F68",
+"t 	c #4D564E",
+"u 	c #4E5950",
+"v 	c #585E57",
+"w 	c #333530",
+"x 	c #9D9C9B",
+"y 	c #FCFDFE",
+"z 	c #FDFEFE",
+"A 	c #FDFDFE",
+"B 	c #F5F5F4",
+"C 	c #B5B5B5",
+"D 	c #F9F9F9",
+"E 	c #FCFCFC",
+"F 	c #CBCBCB",
+"G 	c #A5A5A5",
+"H 	c #CECDCB",
+"I 	c #788976",
+"J 	c #C6BDBB",
+"K 	c #E2E2E2",
+"L 	c #E2E0DF",
+"M 	c #656A65",
+"N 	c #4A4B49",
+"O 	c #EAEDF1",
+"P 	c #CED5DD",
+"Q 	c #DBE0E9",
+"R 	c #DDE1E9",
+"S 	c #D3D7DD",
+"T 	c #D9DCE1",
+"U 	c #F4F4F4",
+"V 	c #ACACAC",
+"W 	c #ECECEC",
+"X 	c #C3C3C3",
+"Y 	c #C2C2C2",
+"Z 	c #C9C9C8",
+"` 	c #A8A8A8",
+" .	c #F6F8F6",
+"..	c #A7ADA5",
+"+.	c #6D7F71",
+"@.	c #ECECE9",
+"#.	c #FDFDFC",
+"$.	c #939C9A",
+"%.	c #1B2321",
+"&.	c #FCFBFA",
+"*.	c #F2F6F9",
+"=.	c #B4BFCC",
+"-.	c #7E8A9B",
+";.	c #515B6F",
+">.	c #50596A",
+",.	c #737D8D",
+"'.	c #BEC6CE",
+").	c #C4CBD5",
+"!.	c #BFC5D1",
+"~.	c #F1F2F4",
+"{.	c #F4F4F3",
+"].	c #ABABAB",
+"^.	c #515151",
+"/.	c #474747",
+"(.	c #464645",
+"_.	c #464646",
+":.	c #5D5D5C",
+"<.	c #A9A9A9",
+"[.	c #FAFAF9",
+"}.	c #EDEFF0",
+"|.	c #F1F5F5",
+"1.	c #636E62",
+"2.	c #636E5F",
+"3.	c #D4D5D3",
+"4.	c #FBF7F4",
+"5.	c #9BA595",
+"6.	c #657366",
+"7.	c #001514",
+"8.	c #FCFAF8",
+"9.	c #FDFCFC",
+"0.	c #FBF9F7",
+"a.	c #E3E6EA",
+"b.	c #9DACBF",
+"c.	c #515A6D",
+"d.	c #111828",
+"e.	c #131B29",
+"f.	c #0F1A2B",
+"g.	c #081125",
+"h.	c #0D1328",
+"i.	c #C2C9D1",
+"j.	c #C5CBD4",
+"k.	c #A6AEBB",
+"l.	c #F6F6F7",
+"m.	c #FAFAFA",
+"n.	c #F2F2F2",
+"o.	c #F3F3F3",
+"p.	c #AAAAAA",
+"q.	c #A0A09F",
+"r.	c #8A8A8A",
+"s.	c #7A7A7A",
+"t.	c #6C6C6C",
+"u.	c #454545",
+"v.	c #ABB3AF",
+"w.	c #A8B6A3",
+"x.	c #D0DBD0",
+"y.	c #F2F3F0",
+"z.	c #DEE1E0",
+"A.	c #66736A",
+"B.	c #354336",
+"C.	c #556254",
+"D.	c #C5C7C6",
+"E.	c #F2EDEA",
+"F.	c #CADAD9",
+"G.	c #9BA893",
+"H.	c #1B3128",
+"I.	c #FCF4EF",
+"J.	c #FDFBF9",
+"K.	c #DDDFE4",
+"L.	c #97A3B4",
+"M.	c #374152",
+"N.	c #0D131C",
+"O.	c #1C2026",
+"P.	c #262C3A",
+"Q.	c #242E43",
+"R.	c #202D41",
+"S.	c #172538",
+"T.	c #121523",
+"U.	c #BABCC3",
+"V.	c #9DA6B5",
+"W.	c #BABEC4",
+"X.	c #F8F8F7",
+"Y.	c #E7E7E7",
+"Z.	c #C6C6C5",
+"`.	c #BABAB9",
+" +	c #E7E9E9",
+".+	c #63736C",
+"++	c #464945",
+"@+	c #5F7361",
+"#+	c #CED4C3",
+"$+	c #DBDEDA",
+"%+	c #979999",
+"&+	c #404342",
+"*+	c #606E6B",
+"=+	c #E5E5E3",
+"-+	c #4B5F52",
+";+	c #455845",
+">+	c #2D362F",
+",+	c #FCFAF9",
+"'+	c #E4E4E7",
+")+	c #97A8B9",
+"!+	c #343C4A",
+"~+	c #13181E",
+"{+	c #1B2027",
+"]+	c #1D232B",
+"^+	c #1F2632",
+"/+	c #212A3C",
+"(+	c #242F45",
+"_+	c #2A374D",
+":+	c #121C2F",
+"<+	c #525A67",
+"[+	c #D9DDE5",
+"}+	c #737C8C",
+"|+	c #EAEBEB",
+"1+	c #F7F7F6",
+"2+	c #F6F6F5",
+"3+	c #F5F5F5",
+"4+	c #EDEDEB",
+"5+	c #FBFBFA",
+"6+	c #6C8175",
+"7+	c #778673",
+"8+	c #121613",
+"9+	c #4C504A",
+"0+	c #8FA695",
+"a+	c #E2DCD8",
+"b+	c #E5E6E7",
+"c+	c #FBFAF9",
+"d+	c #CAC6C1",
+"e+	c #847F79",
+"f+	c #FCFAFA",
+"g+	c #ECE9E9",
+"h+	c #A6B1C4",
+"i+	c #232B37",
+"j+	c #12171E",
+"k+	c #191F27",
+"l+	c #1D222B",
+"m+	c #1E242B",
+"n+	c #1E252F",
+"o+	c #202937",
+"p+	c #232F44",
+"q+	c #283853",
+"r+	c #253046",
+"s+	c #0A1526",
+"t+	c #D5D7DB",
+"u+	c #727D90",
+"v+	c #CFD0D2",
+"w+	c #B1B1B1",
+"x+	c #99A597",
+"y+	c #7B9173",
+"z+	c #65705D",
+"A+	c #1F201B",
+"B+	c #58655E",
+"C+	c #F3F2F1",
+"D+	c #AAB3C1",
+"E+	c #525C6D",
+"F+	c #0A0F17",
+"G+	c #1C2129",
+"H+	c #1E242A",
+"I+	c #1E242C",
+"J+	c #1F232D",
+"K+	c #232E41",
+"L+	c #2B3855",
+"M+	c #2C3B57",
+"N+	c #070D1F",
+"O+	c #A3ABB6",
+"P+	c #828EA0",
+"Q+	c #B9BCC2",
+"R+	c #F3F3F2",
+"S+	c #F1EFEC",
+"T+	c #72886D",
+"U+	c #4E5849",
+"V+	c #748268",
+"W+	c #808080",
+"X+	c #D6D9DE",
+"Y+	c #5E6A7A",
+"Z+	c #121621",
+"`+	c #161D26",
+" @	c #1D242C",
+".@	c #1F242B",
+"+@	c #1E2427",
+"@@	c #212C3F",
+"#@	c #283552",
+"$@	c #2F405D",
+"%@	c #0E162B",
+"&@	c #59657B",
+"*@	c #8A98AA",
+"=@	c #AFB4BC",
+"-@	c #F2F2F1",
+";@	c #7D9074",
+">@	c #494949",
+",@	c #676D65",
+"'@	c #C8CBC7",
+")@	c #818B9B",
+"!@	c #1B232F",
+"~@	c #0F151F",
+"{@	c #1B212B",
+"]@	c #1E232D",
+"^@	c #20252C",
+"/@	c #24272C",
+"(@	c #1F2429",
+"_@	c #1C2228",
+":@	c #1F2A3C",
+"<@	c #1F2A40",
+"[@	c #25344E",
+"}@	c #314464",
+"|@	c #15223A",
+"1@	c #34445E",
+"2@	c #939FB3",
+"3@	c #ACB2BC",
+"4@	c #F1F1F0",
+"5@	c #ECEEEB",
+"6@	c #B8BCB7",
+"7@	c #647C65",
+"8@	c #797E78",
+"9@	c #C4C3C2",
+"0@	c #919191",
+"a@	c #DCE3EA",
+"b@	c #242C3D",
+"c@	c #0B101C",
+"d@	c #1A212A",
+"e@	c #1F242C",
+"f@	c #21252C",
+"g@	c #222629",
+"h@	c #1C232A",
+"i@	c #212B3D",
+"j@	c #33415B",
+"k@	c #27344B",
+"l@	c #212F47",
+"m@	c #374A68",
+"n@	c #1A2840",
+"o@	c #34445F",
+"p@	c #96A3B5",
+"q@	c #B3B7C0",
+"r@	c #F2F2F0",
+"s@	c #F0F0F0",
+"t@	c #E2E7E2",
+"u@	c #939E92",
+"v@	c #50644E",
+"w@	c #3D443E",
+"x@	c #D4D2CF",
+"y@	c #7F8898",
+"z@	c #020A14",
+"A@	c #171E28",
+"B@	c #1C222A",
+"C@	c #1E232B",
+"D@	c #1F242D",
+"E@	c #1F2228",
+"F@	c #1D2328",
+"G@	c #283449",
+"H@	c #4A5C7B",
+"I@	c #697D98",
+"J@	c #4D5D79",
+"K@	c #1F2D45",
+"L@	c #394A6B",
+"M@	c #1B2943",
+"N@	c #425371",
+"O@	c #95A3B5",
+"P@	c #B8BDC2",
+"Q@	c #EFEFEE",
+"R@	c #EFEFED",
+"S@	c #EDEEED",
+"T@	c #EFF0F0",
+"U@	c #F7FCF3",
+"V@	c #313F30",
+"W@	c #1C2120",
+"X@	c #C9CBC9",
+"Y@	c #F9F9F8",
+"Z@	c #E9EEF2",
+"`@	c #3C4352",
+" #	c #0C111D",
+".#	c #1E232E",
+"+#	c #1D212A",
+"@#	c #20242C",
+"##	c #1B1D20",
+"$#	c #263043",
+"%#	c #576886",
+"&#	c #8396B1",
+"*#	c #7A8FAD",
+"=#	c #677B99",
+"-#	c #28374F",
+";#	c #384A69",
+">#	c #182642",
+",#	c #5D708F",
+"'#	c #828FA3",
+")#	c #C5C7CB",
+"!#	c #F1F1EF",
+"~#	c #EEEEED",
+"{#	c #EEEEEC",
+"]#	c #9DA7A4",
+"^#	c #2A3B34",
+"/#	c #222725",
+"(#	c #80807E",
+"_#	c #EFEEEC",
+":#	c #F1F0EF",
+"<#	c #F4F3F2",
+"[#	c #F8F8F8",
+"}#	c #A8AFBB",
+"|#	c #0D1522",
+"1#	c #1A212E",
+"2#	c #1D232C",
+"3#	c #21252D",
+"4#	c #1E2327",
+"5#	c #171B20",
+"6#	c #637593",
+"7#	c #95A6BE",
+"8#	c #6D7F9C",
+"9#	c #617395",
+"0#	c #5A6E8B",
+"a#	c #2A354A",
+"b#	c #3D4E6D",
+"c#	c #1C2A46",
+"d#	c #748DAB",
+"e#	c #6D798A",
+"f#	c #DEDFE0",
+"g#	c #EEEEEE",
+"h#	c #EDEDEC",
+"i#	c #ECEAE7",
+"j#	c #ADB1B0",
+"k#	c #B5B9BA",
+"l#	c #F1F0F0",
+"m#	c #F9F8F8",
+"n#	c #F7F5F4",
+"o#	c #A7AAA4",
+"p#	c #B7BAB4",
+"q#	c #F7F6F6",
+"r#	c #ECEFF1",
+"s#	c #747F8E",
+"t#	c #0A121E",
+"u#	c #232C3A",
+"v#	c #1D232E",
+"w#	c #222932",
+"x#	c #1B1D21",
+"y#	c #1C273E",
+"z#	c #C1CFE1",
+"A#	c #8FA1B7",
+"B#	c #6D81A0",
+"C#	c #596D93",
+"D#	c #455674",
+"E#	c #293343",
+"F#	c #4A5C7D",
+"G#	c #304060",
+"H#	c #7690B8",
+"I#	c #5D6878",
+"J#	c #BBC0CC",
+"K#	c #ECECEB",
+"L#	c #F6F4F3",
+"M#	c #F4F1EF",
+"N#	c #F7F5F3",
+"O#	c #F9F8F7",
+"P#	c #BBBDB9",
+"Q#	c #4D5852",
+"R#	c #71746D",
+"S#	c #F0EAE7",
+"T#	c #DBDEE3",
+"U#	c #3E495B",
+"V#	c #1C2435",
+"W#	c #273041",
+"X#	c #283040",
+"Y#	c #293141",
+"Z#	c #262B38",
+"`#	c #1D212D",
+" $	c #4A5B77",
+".$	c #CFDBEB",
+"+$	c #7A8FAC",
+"@$	c #6C83A5",
+"#$	c #4D6389",
+"$$	c #35445E",
+"%$	c #404D60",
+"&$	c #506586",
+"*$	c #4F6687",
+"=$	c #6983AB",
+"-$	c #343E50",
+";$	c #4D5F7A",
+">$	c #B8C0CC",
+",$	c #F7F6F5",
+"'$	c #D2D6D3",
+")$	c #6C866F",
+"!$	c #5A715E",
+"~$	c #566758",
+"{$	c #B5C1B4",
+"]$	c #B2BAC8",
+"^$	c #273245",
+"/$	c #29354A",
+"($	c #323D53",
+"_$	c #364258",
+":$	c #333F52",
+"<$	c #343F52",
+"[$	c #28354A",
+"}$	c #6D7B92",
+"|$	c #ACBDD2",
+"1$	c #5B7297",
+"2$	c #495F87",
+"3$	c #3A4D6B",
+"4$	c #252F44",
+"5$	c #8292AC",
+"6$	c #586E94",
+"7$	c #6782A9",
+"8$	c #37404D",
+"9$	c #394860",
+"0$	c #475977",
+"a$	c #6E7E99",
+"b$	c #EBEBEA",
+"c$	c #BEC3BE",
+"d$	c #CFD1CE",
+"e$	c #F4F2F0",
+"f$	c #7D8F84",
+"g$	c #6A8667",
+"h$	c #D5D8D4",
+"i$	c #6B6B6B",
+"j$	c #9E9E9F",
+"k$	c #7A8EA8",
+"l$	c #2B374D",
+"m$	c #313E56",
+"n$	c #34425B",
+"o$	c #36425A",
+"p$	c #354257",
+"q$	c #39465D",
+"r$	c #3B4963",
+"s$	c #64738B",
+"t$	c #768CAD",
+"u$	c #354C72",
+"v$	c #2E3D5B",
+"w$	c #1D273A",
+"x$	c #3D4655",
+"y$	c #A7BACE",
+"z$	c #607799",
+"A$	c #617494",
+"B$	c #191B1B",
+"C$	c #445675",
+"D$	c #3A4A65",
+"E$	c #6C7A91",
+"F$	c #ECECEA",
+"G$	c #EAEAE9",
+"H$	c #EAEAE8",
+"I$	c #F7F6F4",
+"J$	c #B5B9B6",
+"K$	c #D7DCD9",
+"L$	c #7B8982",
+"M$	c #A9AEAD",
+"N$	c #517257",
+"O$	c #839284",
+"P$	c #DEDEDD",
+"Q$	c #D8D9D9",
+"R$	c #F3F2EE",
+"S$	c #51698C",
+"T$	c #313F55",
+"U$	c #35435B",
+"V$	c #36445A",
+"W$	c #35425C",
+"X$	c #37465D",
+"Y$	c #3D4A62",
+"Z$	c #516079",
+"`$	c #47536A",
+" %	c #354561",
+".%	c #212F46",
+"+%	c #0F1624",
+"@%	c #1A2231",
+"#%	c #99A5B7",
+"$%	c #97A8C3",
+"%%	c #6A7DA0",
+"&%	c #394559",
+"*%	c #1C1D26",
+"=%	c #35435C",
+"-%	c #344159",
+";%	c #8E96A2",
+">%	c #E9E9E8",
+",%	c #E9E9E7",
+"'%	c #F6F5F5",
+")%	c #70827F",
+"!%	c #C6D0C3",
+"~%	c #445344",
+"{%	c #637161",
+"]%	c #4B6C4C",
+"^%	c #CAC8C8",
+"/%	c #3D5271",
+"(%	c #3A4863",
+"_%	c #384862",
+":%	c #3E4C65",
+"<%	c #424D67",
+"[%	c #45536C",
+"}%	c #4D5A74",
+"|%	c #66768F",
+"1%	c #76869D",
+"2%	c #2A3243",
+"3%	c #151A27",
+"4%	c #252C3A",
+"5%	c #B2BBCE",
+"6%	c #C3D0E3",
+"7%	c #7B8CA9",
+"8%	c #51627E",
+"9%	c #171C22",
+"0%	c #141920",
+"a%	c #242C3A",
+"b%	c #3F4B60",
+"c%	c #BDC0C6",
+"d%	c #EBEBE9",
+"e%	c #D1D7D3",
+"f%	c #4F5753",
+"g%	c #27322D",
+"h%	c #57655B",
+"i%	c #7E8B86",
+"j%	c #F0EFED",
+"k%	c #3E516C",
+"l%	c #3F4E68",
+"m%	c #3C4A64",
+"n%	c #4C5A74",
+"o%	c #55637C",
+"p%	c #5A6881",
+"q%	c #707D95",
+"r%	c #8493A6",
+"s%	c #BFC8D8",
+"t%	c #D8E4F0",
+"u%	c #CCD5E4",
+"v%	c #E2E8F1",
+"w%	c #D2DAE9",
+"x%	c #8D9DB7",
+"y%	c #526380",
+"z%	c #323E53",
+"A%	c #080B11",
+"B%	c #0E1217",
+"C%	c #272E3A",
+"D%	c #747D8A",
+"E%	c #D6D7D8",
+"F%	c #E8E8E7",
+"G%	c #E8E8E6",
+"H%	c #D2D2D0",
+"I%	c #B3B7B4",
+"J%	c #BFC1BF",
+"K%	c #EBE8E7",
+"L%	c #5A718E",
+"M%	c #3F4C65",
+"N%	c #38475F",
+"O%	c #68768E",
+"P%	c #6E7C92",
+"Q%	c #78879B",
+"R%	c #959FB1",
+"S%	c #AAB4C1",
+"T%	c #C4CBD8",
+"U%	c #D7DCE6",
+"V%	c #D3D9E1",
+"W%	c #CCD3DC",
+"X%	c #8C9CB4",
+"Y%	c #52627B",
+"Z%	c #38475E",
+"`%	c #080E1A",
+" &	c #060910",
+".&	c #0E0F14",
+"+&	c #4F5869",
+"@&	c #B6B8BC",
+"#&	c #DDDDDC",
+"$&	c #E2E2E1",
+"%&	c #E5E5E4",
+"&&	c #E6E6E5",
+"*&	c #F2F1F1",
+"=&	c #F2F1F0",
+"-&	c #93A8C2",
+";&	c #2F3E56",
+">&	c #6C7A93",
+",&	c #939EAE",
+"'&	c #A9B3C0",
+")&	c #BDC3CB",
+"!&	c #C8CCD2",
+"~&	c #CDCFD5",
+"{&	c #CBD2DB",
+"]&	c #C7CFDC",
+"^&	c #A4B2C7",
+"/&	c #4C576D",
+"(&	c #262C3C",
+"_&	c #252D3B",
+":&	c #232C3D",
+"<&	c #323B4B",
+"[&	c #464B5A",
+"}&	c #959697",
+"|&	c #BDBDBC",
+"1&	c #C9C9C9",
+"2&	c #D6D6D5",
+"3&	c #E2E2E0",
+"4&	c #E7E7E6",
+"5&	c #E6E6E4",
+"6&	c #C8D6E7",
+"7&	c #515D76",
+"8&	c #33415A",
+"9&	c #66728A",
+"0&	c #C0C7D2",
+"a&	c #CACED7",
+"b&	c #CCCDD2",
+"c&	c #CBCED1",
+"d&	c #C8CED3",
+"e&	c #C2CDDB",
+"f&	c #A2B1C5",
+"g&	c #586377",
+"h&	c #181F27",
+"i&	c #363B40",
+"j&	c #575857",
+"k&	c #626261",
+"l&	c #656463",
+"m&	c #787877",
+"n&	c #929291",
+"o&	c #9F9F9D",
+"p&	c #B0B0AE",
+"q&	c #C3C3C2",
+"r&	c #D1D1D0",
+"s&	c #DCDCDB",
+"t&	c #DFE4EB",
+"u&	c #5F708B",
+"v&	c #717E92",
+"w&	c #4F5E78",
+"x&	c #8391A4",
+"y&	c #A9B4C2",
+"z&	c #B1BBC5",
+"A&	c #BAC3CE",
+"B&	c #D5DDE8",
+"C&	c #9EA7B9",
+"D&	c #444D63",
+"E&	c #1F242F",
+"F&	c #2D2F33",
+"G&	c #424242",
+"H&	c #4D4D4D",
+"I&	c #515150",
+"J&	c #575756",
+"K&	c #60605F",
+"L&	c #747473",
+"M&	c #838381",
+"N&	c #989897",
+"O&	c #B3B3B2",
+"P&	c #C6C6C4",
+"Q&	c #D3D3D2",
+"R&	c #F0EFEE",
+"S&	c #D0D1D3",
+"T&	c #959FB4",
+"U&	c #5B697E",
+"V&	c #9DA7B7",
+"W&	c #D8DEE4",
+"X&	c #D4DAE1",
+"Y&	c #DBE1E5",
+"Z&	c #DBDDE3",
+"`&	c #818894",
+" *	c #1E293A",
+".*	c #1B222B",
+"+*	c #222425",
+"@*	c #313131",
+"#*	c #343434",
+"$*	c #363635",
+"%*	c #3A3A3A",
+"&*	c #40403F",
+"**	c #484847",
+"=*	c #585858",
+"-*	c #6B6B6A",
+";*	c #838382",
+">*	c #A5A5A3",
+",*	c #BCBCBA",
+"'*	c #CFCFCE",
+")*	c #EFEFEF",
+"!*	c #E3E3E2",
+"~*	c #D9D9D8",
+"{*	c #BCBCBB",
+"]*	c #9DA0A4",
+"^*	c #616678",
+"/*	c #2F3A4F",
+"(*	c #2C3649",
+"_*	c #394250",
+":*	c #111923",
+"<*	c #010613",
+"[*	c #0A0F16",
+"}*	c #1F2225",
+"|*	c #262628",
+"1*	c #282828",
+"2*	c #292828",
+"3*	c #292929",
+"4*	c #2D2D2C",
+"5*	c #2F2F2F",
+"6*	c #343433",
+"7*	c #3D3D3C",
+"8*	c #5C5C5C",
+"9*	c #A1A1A0",
+"0*	c #B9B9B8",
+"a*	c #CECECC",
+"b*	c #E4E4E2",
+"c*	c #D7D7D5",
+"d*	c #989896",
+"e*	c #717174",
+"f*	c #464B51",
+"g*	c #2F353C",
+"h*	c #23272C",
+"i*	c #222529",
+"j*	c #25272A",
+"k*	c #2E2F31",
+"l*	c #313030",
+"m*	c #313130",
+"n*	c #333332",
+"o*	c #363636",
+"p*	c #3C3C3C",
+"q*	c #545454",
+"r*	c #686867",
+"s*	c #848483",
+"t*	c #A8A8A7",
+"u*	c #C0C0BE",
+"v*	c #D3D3D1",
+"w*	c #D9D9D7",
+"x*	c #C0C0BF",
+"y*	c #A3A3A2",
+"z*	c #888886",
+"A*	c #6E6D6C",
+"B*	c #5F6061",
+"C*	c #585859",
+"D*	c #525252",
+"E*	c #A3A3A1",
+"F*	c #A2A2A0",
+"G*	c #A2A1A0",
+"H*	c #A2A2A1",
+"I*	c #A3A2A1",
+"J*	c #A4A3A2",
+"K*	c #A4A4A2",
+"L*	c #A6A6A4",
+"M*	c #A9A8A6",
+"N*	c #ABAAA8",
+"O*	c #AFAEAB",
+"P*	c #B3B1AE",
+"Q*	c #B5B4B1",
+"R*	c #B8B6B2",
+"S*	c #BAB8B4",
+"T*	c #6E6B62",
+"U*	c #DEDEDC",
+"V*	c #D1D1CF",
+"W*	c #BFBFBE",
+"X*	c #AFAFAF",
+"Y*	c #9F9F9F",
+"Z*	c #919190",
+"`*	c #888888",
+" =	c #302E28",
+".=	c #2E2C27",
+"+=	c #2D2B26",
+"@=	c #312F29",
+"#=	c #33312B",
+"$=	c #36332D",
+"%=	c #38362F",
+"&=	c #3D3A33",
+"*=	c #413E36",
+"==	c #47433B",
+"-=	c #4D4940",
+";=	c #514D44",
+">=	c #535046",
+",=	c #565248",
+"'=	c #23211D",
+")=	c #EBEAE9",
+"!=	c #DADAD9",
+"~=	c #C9C9C7",
+"{=	c #B6B6B6",
+"]=	c #B2B0AD",
+"^=	c #423F37",
+"/=	c #E3E2E1",
+"(=	c #4E4B44",
+"_=	c #403D36",
+":=	c #413E37",
+"<=	c #908E8A",
+"[=	c #46433B",
+"}=	c #D8D7D5",
+"|=	c #75726A",
+"1=	c #ACAAA5",
+"2=	c #D7D6D3",
+"3=	c #DBDAD8",
+"4=	c #86837C",
+"5=	c #D5D5D4",
+"6=	c #B6B5B1",
+"7=	c #4E4A41",
+"8=	c #D5D4D2",
+"9=	c #514E46",
+"0=	c #5E5B52",
+"a=	c #504D43",
+"b=	c #545147",
+"c=	c #8A8780",
+"d=	c #E3E3E1",
+"e=	c #D2D1CE",
+"f=	c #7B7770",
+"g=	c #58544A",
+"h=	c #7C7971",
+"i=	c #E7E7E5",
+"j=	c #CCCBC8",
+"k=	c #C4C2BF",
+"l=	c #C4C3BF",
+"m=	c #FBFAFA",
+"n=	c #817E76",
+"o=	c #D1D0CD",
+"p=	c #67635A",
+"q=	c #F5F4F4",
+"r=	c #67645B",
+"s=	c #908E87",
+"t=	c #E5E4E3",
+"u=	c #6F6C63",
+"v=	c #8B8881",
+"w=	c #C0BEBA",
+"x=	c #8C8982",
+"y=	c #C8C7C4",
+"z=	c #656259",
+"A=	c #77736B",
+"B=	c #E4E4E3",
+"C=	c #D3D2CF",
+"D=	c #76736B",
+"E=	c #59554B",
+"F=	c #E7E6E5",
+"G=	c #AFADA8",
+"H=	c #DFDFDD",
+"I=	c #D0CFCC",
+"J=	c #736F67",
+"K=	c #A9A9A7",
+"L=	c #8D8D8D",
+"M=	c #E1E1DF",
+"N=	c #9A9A9A",
+"O=	c #8C8C8C",
+"  . . . . . . . . . . . . . + @ # + . . . . . . . . . . . . . . . . . $ %                       ",
+". & * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * = - ; .                   ",
+". * * * * * * * * * * * * * * * * * * * > * * , , , , , , , , , , , , ' = ) ! .                 ",
+". * * * * * * ~ * * { ] ^ / ( _ : < * * * , [ [ , , , , , , , , , , , } | * 1 2 .               ",
+". * * * * * * ~ * * 3 4 5 6 7 8 9 0 * , , , , , , , , , , , , , , , , } a * * ) b c             ",
+". * * * * * * * * * d e f g h i j k , , , , , l , , , , , , , , , m m = n * m * o p q           ",
+". * * * * * * * * * * r s t u v w x , , , , , l , , y z A m m m m m m B C * D E * F G .         ",
+". * * * * * * * * * * H I J K L M N , , , , l m z O P Q R S T ' E E E U V W X F n Y Z ` .       ",
+". * * * * * * * * m  ...+.@., #.$.%., , , , &.*.=.-.;.>.,.'.).!.~.E d {.].^./.(._._.:.<..       ",
+". * * * * [. .m * }.|.1.2.3.4.5.6.7.8.m 9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.! p.q.r.s.t.u..       ",
+". * * , n.v.w.x.y.z.A.B.C.D.E.F.G.H.I.m J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.D ' } } Y.Z.Z `.s..       ",
+". * * ,  +.+++@+#+$+%+&+*+=+E -+;+>+,+,+'+)+!+~+{+]+^+/+(+_+:+<+[+}+|+} X.1+2+3+3+3+4+<..       ",
+". * * , 5+6+7+8+9+0+1 a+b+c+E d+e+` f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+= } 3+3+B B o.o.w+.       ",
+". * , , 9.x+y+z+A+B+c+E E E E E E d C+D+E+F+G+l+m+H+I+J+K+L+M+N+O+P+Q+B B B U {.o.R+R+C .       ",
+". * m m m S+T+U+V+W+X.E E d ' ' ' ' X+Y+Z+`+ @I+.@.@+@^+@@#@$@%@&@*@=@o.U {.R+R+R+-@-@C .       ",
+". * m #.E ' ;@>@,@'@5+' ' ' ' 5+5+m.)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@{.R+R+n.n.-@4@4@C .       ",
+". * E ' 5@6@7@8@9@0@} ' 5+5+m.m.m.a@b@c@d@l+e@f@g@h@i@j@k@l@m@n@o@p@q@n.n.-@r@4@4@s@s@C .       ",
+". * d [.t@u@v@w@x@{.m.m.m.m.m.[.m.y@z@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@-@-@r@s@s@< Q@R@C .       ",
+". * ' S@T@U@V@W@X@m.m.[.[.[.D Y@Z@`@ #.#+#@#D@##$#%#&#*#=#-#;#>#,#'#)#4@!#< < Q@~#~#{#C .       ",
+". * 5+3+]#^#/#(#_#[.D :#<#D X.[#}#|#1#2#3#f@4#5#6#7#8#9#0#a#b#c#d#e#f#< < g#g#~#{#h#h#C .       ",
+". * m.X.i#j#k#l#m#D n#o#p#q#} r#s#t#u#v#w#^@x#y#z#A#B#C#D#E#F#G#H#I#J#|+~#~#{#h#h#W K#C .       ",
+". * [.L#M#N#[#O#O#1+P#Q#R#S#1+T#U#V#W#X#Y#Z#`# $.$+$@$#$$$%$&$*$=$-$;$>${#h#4+4+K#K#K#C .       ",
+". * D ,$X.Y@[#[#} '$)$!$~${$3+]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$7$8$9$0$a$4+4+K#K#b$b$b$C .       ",
+". * [#[#[#n#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$K#F$b$b$b$G$H$C .       ",
+". * X.1+I$J$K$L$M$N$O$< P$Q$R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*%=%-%;%b$b$b$G$>%>%,%C .       ",
+". * = = '%)%!%~%{%]%^%o.R+R+{./%(%_%:%<%[%}%|%1%2%3%4%5%6%7%8%9%0%a%b%c%d%>%>%>%,%,%,%C .       ",
+". * 2+2+2+e%f%g%h%i%j%R+-@1 -@k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z%A%B%C%D%E%F%F%>%,%F%F%G%C .       ",
+". * 3+U {.{.H%I%J%K%-@4@4@4@4@L%M%N%O%P%Q%R%S%T%U%V%W%X%Y%Z%`% &.&+&@&#&$&%&&&F%G%G%G%C .       ",
+". * {.{.R+n.*&=&=&4@4@4@s@< Q@-&M%;&>&,&'&)&!&~&{&]&^&/&(&_&:&<&[&}&|&1&2&#&3&4&4&4&5&C .       ",
+". * n.-@-@-@-@4@4@s@< < < ~#b$6&7&8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&4&5&5&%&C .       ",
+". * -@-@-@4@< < < < < Q@R@G$%&t&u&v&w&x&y&z&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&Q&%&%&%&%&C .       ",
+". * 1 4@< < j%R&Q@Q@R@~#h#&&P$S&T&U&V&W&X&Y&Z&`& *.*+*@*#*$*%*&***=*-*;*>*,*'*%&=+=+=+C .       ",
+". * < < )*Q@Q@Q@R@~#~#K#G$!*~*{*]*^*/*(*_*:*<*[*}*|*1*2*3*4*5*6*7*>@8*m&9*0*a*=+=+=+b*C .       ",
+". * Q@Q@Q@Q@R@~#{#K#K#K#>%!*c*,*d*e*f*g*h*i*j*k*l*m*5*@*@*n*o*p*u.q*r*s*t*u*v*=+=+b*b*C .       ",
+". * Q@Q@~#~#{#K#K#K#K#b$>%!*w*x*y*z*A*B*C*D*E*F*F*F*G*F*H*I*J*K*L*M*N*O*P*Q*R*S*S*S*S*S*S*S*S*T*",
+". * ~#~#4+K#K#F$b$b$b$b$H$5&U*V*W*X*Y*N&Z*`*N* =.=+=+=.= =@=#=$=%=&=*===-=;=>=,=,=,=,=,=,=,=,='=",
+". * 4+K#F$)=)=)=b$d%d%G$H$G%b*!=H%~=x*{*0*{=]=^=* /=(=_=:=<=* * [=* * * m.}=|=,=1=2=D X.3=4=,='=",
+". * F$b$b$b$d%G$G$G$G$H$H$G%5&3&#&~*5=Q&Q&H%6=7=* * 8=9=0={.* * a=* * b=c=* d=,=e=f=g=h=* R+,='=",
+". * d%d%G$G$G$G$G$H$H$G%G%G%G%i=i=i=i=i=&&5&S*,=* j=* k=l=j=* * ,=* * ,=g=* m=,=,=,=,=n=* o=,='=",
+". * G$G$G$H$H$>%>%F%G%G%G%i=i=i=i=i=5&5&=+=+S*,=* p={.* q=r=* * ,=* * ,=s=* o=,=,=,=* * t=u=,='=",
+". * H$>%>%>%>%F%G%G%4&4&i=i=i=i=5&5&=+=+=+=+S*,=* ,=v=w=x=,=* * ,=* * * m#y=z=,=,=,=,=A=* )=,='=",
+". * >%>%>%G%4&4&4&4&i=i=i=&&&&5&=+=+=+=+B=B=S*,=* ,=,=,=,=,=* * ,=* * ,=,=,=,=,=C=D=E=4=* F=,='=",
+". * ,%4&4&4&4&i=i=i=&&&&&&5&=+=+=+B=B=b*b*b*S*,=* ,=,=,=,=,=* * ,=* * ,=,=,=,=,=G=H=m=q#I=J=,='=",
+". * 4&i=i=i=&&&&&&&&5&5&=+B=B=B=b*b*b*b*b*b*S*,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,='=",
+". * i=&&&&&&5&5&5&5&B=B=b*b*b*b*b*b*b*b*b*d=T*'='='='='='='='='='='='='='='='='='='='='='='='='=",
+". * &&5&5&5&5&%&B=b*b*b*b*b*b*b*b*b*b*d=d=d=d=K=p p p y*y*y*y*E*E*E*E*E*E*E*E*E*E*E*E*L=.       ",
+". & 5&5&5&=+b*b*b*b*b*b*b*b*b*b*!*d=d=d=d=d=d=d=d=3&3&3&3&M=M=M=M=M=M=M=M=M=M=M=M=M=M=N=.       ",
+". Y C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C N=O=.       ",
+"  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                "};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/plugin.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,568 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "plugin.h"
+#include "input.h"
+
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <audacious/util.h>
+#include <audacious/configdb.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <audacious/vfs.h>
+#include <sys/stat.h>
+
+/*
+ * Global variables
+ */
+struct audmad_config_t audmad_config;   /**< global configuration */
+static GStaticMutex mutex;
+InputPlugin *mad_plugin = NULL;
+
+/*
+ * static variables
+ */
+static GThread *decode_thread; /**< the single decoder thread */
+static struct mad_info_t info;   /**< info for current track */
+
+#ifndef NOGUI
+static GtkWidget *error_dialog = 0;
+#endif
+
+extern gboolean scan_file(struct mad_info_t *info, gboolean fast);
+
+/*
+ * Function extname (filename)
+ *
+ *    Return pointer within filename to its extenstion, or NULL if
+ *    filename has no extension.
+ *
+ */
+static gchar *extname(const char *filename)
+{
+    gchar *ext = strrchr(filename, '.');
+
+    if (ext != NULL)
+        ++ext;
+
+    return ext;
+}
+
+void audmad_config_compute(struct audmad_config_t *config)
+{
+    /* set some config parameters by parsing text fields 
+       (RG default gain, etc..)
+     */
+    const gchar *text;
+    gdouble x;
+
+    text = config->pregain_db;
+    x = g_strtod(text, NULL);
+    config->pregain_scale = (x != 0) ? pow(10.0, x / 20) : 1;
+#ifdef DEBUG
+    g_message("pregain=[%s] -> %g  -> %g", text, x, config->pregain_scale);
+#endif
+    text = config->replaygain.default_db;
+    x = g_strtod(text, NULL);
+    config->replaygain.default_scale = (x != 0) ? pow(10.0, x / 20) : 1;
+#ifdef DEBUG
+    g_message("RG.default=[%s] -> %g  -> %g", text, x,
+              config->replaygain.default_scale);
+#endif
+}
+
+static void audmad_init()
+{
+    ConfigDb *db = NULL;
+
+    audmad_config.fast_play_time_calc = TRUE;
+    audmad_config.use_xing = TRUE;
+    audmad_config.dither = TRUE;
+    audmad_config.pregain_db = "+0.00";
+    audmad_config.replaygain.enable = TRUE;
+    audmad_config.replaygain.track_mode = FALSE;
+    audmad_config.hard_limit = FALSE;
+    audmad_config.replaygain.default_db = "-9.00";
+
+    db = bmp_cfg_db_open();
+    if (db) {
+        bmp_cfg_db_get_bool(db, "MAD", "fast_play_time_calc",
+                            &audmad_config.fast_play_time_calc);
+        bmp_cfg_db_get_bool(db, "MAD", "use_xing",
+                            &audmad_config.use_xing);
+        bmp_cfg_db_get_bool(db, "MAD", "dither", &audmad_config.dither);
+        bmp_cfg_db_get_bool(db, "MAD", "hard_limit",
+                            &audmad_config.hard_limit);
+        bmp_cfg_db_get_string(db, "MAD", "pregain_db",
+                              &audmad_config.pregain_db);
+        bmp_cfg_db_get_bool(db, "MAD", "RG.enable",
+                            &audmad_config.replaygain.enable);
+        bmp_cfg_db_get_bool(db, "MAD", "RG.track_mode",
+                            &audmad_config.replaygain.track_mode);
+        bmp_cfg_db_get_string(db, "MAD", "RG.default_db",
+                              &audmad_config.replaygain.default_db);
+        bmp_cfg_db_get_bool(db, "MAD", "title_override",
+                            &audmad_config.title_override);
+        bmp_cfg_db_get_string(db, "MAD", "id3_format",
+                              &audmad_config.id3_format);
+
+        bmp_cfg_db_close(db);
+    }
+
+    g_static_mutex_init(&mutex);
+    audmad_config_compute(&audmad_config);
+
+}
+
+static void audmad_cleanup()
+{
+}
+
+static gboolean mp3_head_check(guint32 head)
+{
+    /*
+     * First two bytes must be a sync header (11 bits all 1)
+     * http://www.mp3-tech.org/programmer/frame_header.html
+     */
+    if ((head & 0xffe00000) != 0xffe00000)
+        return FALSE;
+
+    /* check if bits 18 and 19 are set */
+    if (!((head >> 17) & 3))
+        return FALSE;
+
+    /* check if bits 13 - 16 are all set */
+    if (((head >> 12) & 0xf) == 0xf)
+        return FALSE;
+
+    /* check if bits 13 - 16 are all not set */
+    if (!((head >> 12) & 0xf))
+        return FALSE;
+
+    /* check if bit 11 and 12 are both set */
+    if (((head >> 10) & 0x3) == 0x3)
+        return FALSE;
+
+    /* check if bits 17 - 20 are all set */
+    if (((head >> 19) & 1) == 1 &&
+        ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1)
+        return FALSE;
+
+    /* not sure why we check this, but ok! */
+    if ((head & 0xffff0000) == 0xfffe0000)
+        return FALSE;
+
+    return TRUE;
+}
+
+static int mp3_head_convert(const guchar * hbuf)
+{
+    return ((unsigned long) hbuf[0] << 24) |
+        ((unsigned long) hbuf[1] << 16) |
+        ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3];
+}
+
+// audacious vfs fast version
+static int audmad_is_our_fd(char *filename, VFSFile *fin)
+{
+    guint32 check;
+    gchar *ext = extname(filename);
+    gint cyc = 0;
+    guchar buf[4];
+    guchar tmp[4096];
+    gint ret, i;
+
+    /* I've seen some flac files beginning with id3 frames..
+       so let's exclude known non-mp3 filename extensions */
+    if (!strcasecmp(".flac", ext) || !strcasecmp(".mpc", ext) ||
+        !strcasecmp(".tta", ext))
+        return 0;
+
+    if (fin == NULL)
+        return FALSE;
+
+    vfs_fread(buf, 1, 4, fin);
+
+    check = mp3_head_convert(buf);
+
+    if (memcmp(buf, "ID3", 3) == 0)
+        return 1;
+    else if (memcmp(buf, "RIFF", 4) == 0)
+    {
+        vfs_fseek(fin, 4, SEEK_CUR);
+        vfs_fread(buf, 1, 4, fin);
+
+        if (memcmp(buf, "RMP3", 4) == 0)
+            return 1;
+    }
+
+    while (!mp3_head_check(check))
+    {
+        ret = vfs_fread(tmp, 1, 4096, fin);
+	if (ret == 0)
+            return 0;
+
+        for (i = 0; i < ret; i++)
+        {
+            check <<= 8;
+            check |= tmp[i];
+
+            if (mp3_head_check(check))
+                return 1;
+        }
+
+        if (++cyc > 1024)
+            return 0;
+    }
+
+    return 1;
+}
+
+// audacious vfs version
+static int audmad_is_our_file(char *filename)
+{
+    VFSFile *fin = NULL;
+    gint rtn;
+
+    fin = vfs_fopen(filename, "rb");
+
+    if (fin == NULL)
+        return 0;
+
+    rtn = audmad_is_our_fd(filename, fin);
+    vfs_fclose(fin);
+
+    return rtn;
+}
+
+static void audmad_stop(InputPlayback *playback)
+{
+#ifdef DEBUG
+    g_message("f: audmad_stop");
+#endif                          /* DEBUG */
+    g_static_mutex_lock(&mutex);
+    if (decode_thread) {
+        info.playback->playing = 0;
+#ifdef DEBUG
+        g_message("waiting for thread");
+#endif                          /* DEBUG */
+        g_thread_join(decode_thread);
+#ifdef DEBUG
+        g_message("thread done");
+#endif                          /* DEBUG */
+        input_term(&info);
+        decode_thread = NULL;
+    }
+    g_static_mutex_unlock(&mutex);
+}
+
+
+static void audmad_play_file(InputPlayback *playback)
+{
+    gboolean rtn;
+    gchar *url = playback->filename;
+
+#ifdef DEBUG
+    g_message("playing %s", url);
+#endif                          /* DEBUG */
+
+    if (input_init(&info, url) == FALSE) {
+        g_message("error initialising input");
+        return;
+    }
+
+    rtn = input_get_info(&info, audmad_config.fast_play_time_calc);
+
+    if (rtn == FALSE) {
+        g_message("error reading input info");
+        return;
+    }
+
+    info.playback = playback;
+    info.playback->playing = 1;
+
+    decode_thread = g_thread_create(decode_loop, (void *) &info, TRUE, NULL);
+}
+
+static void audmad_pause(InputPlayback *playback, short paused)
+{
+    mad_plugin->output->pause(paused);
+}
+
+static void audmad_seek(InputPlayback *playback, int time)
+{
+    /* xmms gives us the desired seek time in seconds */
+    info.seek = time;
+}
+
+/**
+ * Scan the given file or URL.
+ * Fills in the title string and the track length in milliseconds.
+ */
+void audmad_get_song_info(char *url, char **title, int *length)
+{
+    struct mad_info_t myinfo;
+#ifdef DEBUG
+    g_message("f: audmad_get_song_info: %s", url);
+#endif                          /* DEBUG */
+
+    input_init(&myinfo, url);
+
+    if (input_get_info(&myinfo, audmad_config.fast_play_time_calc) == TRUE)
+    {
+        *title = strdup(myinfo.title);
+        *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS);
+    }
+    else
+    {
+        *title = strdup(url);
+        *length = -1;
+    }
+
+    input_term(&myinfo);
+
+#ifdef DEBUG
+    g_message("e: audmad_get_song_info");
+#endif                          /* DEBUG */
+}
+
+static void audmad_about()
+{
+    static GtkWidget *aboutbox;
+    gchar *scratch;
+
+    if (aboutbox != NULL)
+        return;
+
+    scratch = g_strdup_printf(
+	"Audacious MPEG Audio Plugin\n"
+	"\n"
+	"Compiled against libMAD version: %d.%d.%d%s\n"
+	"\n"
+	"Written by:\n"
+	"    William Pitcock <nenolod@sacredspiral.co.uk>\n"
+	"    Yoshiki Yazawa <yaz@cc.rim.or.jp>\n"
+	"\n"
+	"Portions derived from XMMS-MAD by:\n"
+	"    Sam Clegg\n"
+	"\n"
+	"ReplayGain support by:\n"
+	"    Samuel Krempp",
+	MAD_VERSION_MAJOR, MAD_VERSION_MINOR, MAD_VERSION_PATCH,
+	MAD_VERSION_EXTRA);
+
+    aboutbox = xmms_show_message("About MPEG Audio Plugin",
+                                 scratch,
+                                 "Ok", FALSE, NULL, NULL);
+
+    g_free(scratch);
+
+    g_signal_connect(G_OBJECT(aboutbox), "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &aboutbox);
+}
+
+/**
+ * Display a GTK box containing the given error message.
+ * Taken from mpg123 plugin.
+ */
+void audmad_error(char *error, ...)
+{
+#ifndef NOGUI
+    if (!error_dialog) {
+        va_list args;
+        char string[256];
+        va_start(args, error);
+        vsnprintf(string, 256, error, args);
+        va_end(args);
+        GDK_THREADS_ENTER();
+        error_dialog =
+            xmms_show_message("Error", string, "Ok", FALSE, 0, 0);
+        gtk_signal_connect(GTK_OBJECT(error_dialog), "destroy",
+                           GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+                           &error_dialog);
+        GDK_THREADS_LEAVE();
+    }
+#endif                          /* !NOGUI */
+}
+
+extern void audmad_get_file_info(char *filename);
+extern void audmad_configure();
+
+// tuple stuff
+static TitleInput *audmad_get_song_tuple(char *filename)
+{
+    TitleInput *tuple = NULL;
+    gchar *string = NULL;
+    VFSFile *file;
+
+    struct id3_file *id3file = NULL;
+    struct id3_tag *tag = NULL;
+
+#ifdef DEBUG
+    string = str_to_utf8(filename);
+    g_message("f: mad: audmad_get_song_tuple: %s", string);
+    if (string) {
+        g_free(string);
+        string = NULL;
+    }
+#endif
+
+    if ((file = vfs_fopen(filename, "rb")) != NULL) {
+        tuple = bmp_title_input_new();
+
+        id3file = id3_file_open(filename, ID3_FILE_MODE_READONLY);
+
+        if (id3file) {
+            tag = id3_file_tag(id3file);
+
+            if (tag) {
+                tuple->performer =
+                    input_id3_get_string(tag, ID3_FRAME_ARTIST);
+                tuple->album_name =
+                    input_id3_get_string(tag, ID3_FRAME_ALBUM);
+                tuple->track_name =
+                    input_id3_get_string(tag, ID3_FRAME_TITLE);
+
+                // year
+                string = NULL;
+                string = input_id3_get_string(tag, ID3_FRAME_YEAR); //TDRC
+                if (!string)
+                    string = input_id3_get_string(tag, "TYER");
+
+                if (string) {
+                    tuple->year = atoi(string);
+                    g_free(string);
+                    string = NULL;
+                }
+
+                tuple->file_name = g_path_get_basename(filename);
+                tuple->file_path = g_path_get_dirname(filename);
+                tuple->file_ext = extname(filename);
+
+                // length
+                {
+                    char *dummy = NULL;
+                    int length = 0;
+                    audmad_get_song_info(filename, &dummy, &length);
+                    tuple->length = length;
+		    g_free(dummy);
+                }
+
+                // track number
+                string = input_id3_get_string(tag, ID3_FRAME_TRACK);
+                if (string) {
+                    tuple->track_number = atoi(string);
+                    g_free(string);
+                    string = NULL;
+                }
+                // genre
+                tuple->genre = input_id3_get_string(tag, ID3_FRAME_GENRE);
+#ifdef DEBUG
+                g_message("genre = %s\n", tuple->genre);
+#endif
+                // comment
+                tuple->comment =
+                    input_id3_get_string(tag, ID3_FRAME_COMMENT);
+
+                // mtime
+//                        tuple->mtime = audmad_get_mtime(filename);
+
+            }
+            id3_file_close(id3file);
+        }
+        vfs_fclose(file);
+    }
+#ifdef DEBUG
+    g_message("e: mad: audmad_get_song_tuple");
+#endif
+    tuple->formatter = NULL;    //ensure
+    return tuple;
+
+}
+
+/**
+ * Retrieve meta-information about URL.
+ * For local files this means ID3 tag etc.
+ */
+gboolean mad_get_info(struct mad_info_t * info, gboolean fast_scan)
+{
+    TitleInput *tuple = NULL;
+
+    g_message("f: mad_get_info: %s", info->filename);
+
+    if (info->remote)
+        return TRUE;
+
+    tuple = audmad_get_song_tuple(info->filename);
+    info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ?
+	audmad_config.id3_format : xmms_get_gentitle_format(), tuple);
+
+    /* scan mp3 file, decoding headers unless fast_scan is set */
+    if (scan_file(info, fast_scan) == FALSE)
+        return FALSE;
+
+    /* reset the input file to the start */
+    vfs_rewind(info->infile);
+    info->offset = 0;
+
+    /* use the filename for the title as a last resort */
+    if (!info->title)
+    {
+        char *pos = strrchr(info->filename, '/');
+        if (pos)
+            info->title = g_strdup(pos + 1);
+        else
+            info->title = g_strdup(info->filename);
+    }
+
+    g_message("e: mad_get_info");
+    return TRUE;
+}
+
+static gchar *fmts[] = { "mp3", "mp2", "mpg", NULL };
+
+InputPlugin *get_iplugin_info(void)
+{
+    if (mad_plugin != NULL)
+        return mad_plugin;
+
+    mad_plugin = g_new0(InputPlugin, 1);
+    mad_plugin->description = g_strdup(_("MPEG Audio Plugin"));
+    mad_plugin->init = audmad_init;
+    mad_plugin->about = audmad_about;
+    mad_plugin->configure = audmad_configure;
+    mad_plugin->is_our_file = audmad_is_our_file;
+    mad_plugin->play_file = audmad_play_file;
+    mad_plugin->stop = audmad_stop;
+    mad_plugin->pause = audmad_pause;
+    mad_plugin->seek = audmad_seek;
+    mad_plugin->cleanup = audmad_cleanup;
+    mad_plugin->get_song_info = audmad_get_song_info;
+    mad_plugin->file_info_box = audmad_get_file_info;
+    mad_plugin->get_song_tuple = audmad_get_song_tuple;
+    mad_plugin->is_our_file_from_vfs = audmad_is_our_fd;
+    mad_plugin->vfs_extensions = fmts;
+
+    return mad_plugin;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/plugin.h	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,131 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef AUD_MAD_H
+#define AUD_MAD_H
+
+//#define DEBUG 1
+#undef DEBUG
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "MADPlug"
+
+#include "config.h"
+
+#undef PACKAGE
+#define PACKAGE "audacious-plugins"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <audacious/plugin.h>
+#include <audacious/titlestring.h>
+#include <audacious/strings.h>
+#include <audacious/i18n.h>
+#include <id3tag.h>
+#include <mad.h>
+
+#include "xing.h"
+
+struct mad_info_t
+{
+    InputPlayback *playback;
+    int seek;
+
+    /* state */
+    guint current_frame;/**< current mp3 frame */
+    mad_timer_t pos;    /**< current play position */
+
+    /* song info */
+    guint vbr;      /**< bool: is vbr? */
+    guint bitrate;  /**< avg. bitrate */
+    guint freq;     /**< sample freq. */
+    guint mpeg_layer;   /**< mpeg layer */
+    guint mode;     /**< mpeg stereo mode */
+    guint channels;
+    gint frames;    /**< total mp3 frames or -1 */
+    gint fmt;       /**< sample format */
+    gint size;      /**< file size in bytes or -1 */
+    gchar *title;   /**< title for xmms */
+    mad_timer_t duration;
+            /**< total play time */
+    struct id3_tag *tag;
+    struct id3_file *id3file;
+    struct xing xing;
+    TitleInput *tuple;          /* audacious tuple data */
+
+    /* replay parameters */
+    gboolean has_replaygain;
+    double replaygain_album_scale;  // -1 if not set
+    double replaygain_track_scale;
+    gchar *replaygain_album_str;
+    gchar *replaygain_track_str;
+    double replaygain_album_peak;   // -1 if not set
+    double replaygain_track_peak;
+    gchar *replaygain_album_peak_str;
+    gchar *replaygain_track_peak_str;
+    double mp3gain_undo;        // -1 if not set
+    double mp3gain_minmax;
+    gchar *mp3gain_undo_str;
+    gchar *mp3gain_minmax_str;
+
+    /* data access */
+    gchar *url;
+    gchar *filename;
+    VFSFile *infile;
+    gint offset;
+
+    gint remote;
+    struct streamdata_t *sdata;
+                  /**< stream data for remote connections */
+};
+
+struct audmad_config_t
+{
+    gint http_buffer_size;
+    gboolean fast_play_time_calc;
+    gboolean use_xing;
+    gboolean dither;
+    gboolean hard_limit;
+    gchar *pregain_db;          // gain applied to samples at decoding stage.
+    gdouble pregain_scale;      // pow(10, pregain/20)
+    struct
+    {
+        gboolean enable;
+        gboolean track_mode;
+        gchar *default_db;      // gain used if no RG.
+        gdouble default_scale;
+    } replaygain;
+    gboolean title_override;
+    gchar *id3_format;
+};
+
+void audmad_config_compute(struct audmad_config_t *config);
+// compute scale values from "_db" strings
+
+extern gpointer decode_loop(gpointer arg);
+extern void audmad_error(gchar * fmt, ...);
+extern void audmad_configure();
+extern InputPlugin *mad_plugin;
+extern struct audmad_config_t audmad_config;
+
+#endif                          /* !AUD_MAD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/replaygain.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,254 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ * Copyright (C) 2001-2007 Samuel Krempp
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "plugin.h"
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include <assert.h>
+#include "replaygain.h"
+
+static unsigned long Read_LE_Uint32(const unsigned char *p)
+{
+    return ((unsigned long) p[0] << 0) |
+        ((unsigned long) p[1] << 8) |
+        ((unsigned long) p[2] << 16) | ((unsigned long) p[3] << 24);
+}
+
+static int uncase_strcmp(const char *s1, const char *s2)
+{
+    int l1 = strlen(s1);
+    int l2 = strlen(s2);
+    int i;
+    for (i = 0; i < l1 && i < l2; ++i) {
+        if (toupper(s1[i]) < toupper(s2[i]))
+            return -1;
+    }
+    if (l1 == l2)
+        return 0;
+    return (l1 < l2) ? -1 : +1;
+}
+
+static gdouble strgain2double(gchar * s, int len)
+{
+    gdouble res = g_strtod(s, NULL);    // gain, in dB.
+    if (res == 0)
+        return 1;
+    return pow(10, res / 20);
+}
+
+// Reads APE v2.0 tag ending at current pos in fp
+
+static int ReadAPE2Tag(VFSFile * fp, struct mad_info_t *file)
+{
+    unsigned long vsize;
+    unsigned long isize;
+    unsigned long flags;
+    char *buff;
+    char *p;
+    char *end;
+    struct APETagFooterStruct T, *tp;
+    unsigned long TagLen;
+    unsigned long TagCount;
+
+    tp = &T;
+
+    if (vfs_fseek(fp, -sizeof(T), SEEK_CUR) != 0)
+        return 18;
+    if (vfs_fread(tp, 1, sizeof(T), fp) != sizeof T)
+        return 2;
+    if (memcmp(tp->ID, "APETAGEX", sizeof(tp->ID)) != 0)
+        return 3;
+    if (Read_LE_Uint32(tp->Version) != 2000)
+        return 4;
+    TagLen = Read_LE_Uint32(tp->Length);
+    if (TagLen < sizeof(T))
+        return 5;
+    if (vfs_fseek(fp, -TagLen, SEEK_CUR) != 0)
+        return 6;
+    if ((buff = (char *) malloc(TagLen)) == NULL) {
+        return 7;
+    }
+    if (vfs_fread(buff, 1, TagLen - sizeof(T), fp) != TagLen - sizeof(T)) {
+        free(buff);
+        return 8;
+    }
+#ifdef DEBUG
+    printf("ver = %ld\n", Read_LE_Uint32(tp->Version));
+    printf("taglen = %ld\n", TagLen);
+#endif
+
+    TagCount = Read_LE_Uint32(tp->TagCount);
+    end = buff + TagLen - sizeof(T);
+    for (p = buff; p < end && TagCount--;) {
+        vsize = Read_LE_Uint32((unsigned char *)p);
+        p += 4;
+        flags = Read_LE_Uint32((unsigned char *)p);
+        p += 4;
+        isize = strlen((char *) p);
+
+        if (isize > 0 && vsize > 0) {
+            gdouble *scale = NULL;
+            gchar **str = NULL;
+            if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_GAIN") == 0) {
+                scale = &file->replaygain_album_scale;
+                str = &file->replaygain_album_str;
+            }
+            if (uncase_strcmp(p, "REPLAYGAIN_TRACK_GAIN") == 0) {
+                scale = &file->replaygain_track_scale;
+                str = &file->replaygain_track_str;
+            }
+            if (str != NULL) {
+                assert(scale != NULL);
+                *scale = strgain2double(p + isize + 1, vsize);
+                *str = g_strndup(p + isize + 1, vsize);
+            }
+            //* case of peak info tags : */
+            str = NULL;
+            if (uncase_strcmp(p, "REPLAYGAIN_TRACK_PEAK") == 0) {
+                scale = &file->replaygain_track_peak;
+                str = &file->replaygain_track_peak_str;
+            }
+            if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_PEAK") == 0) {
+                scale = &file->replaygain_album_peak;
+                str = &file->replaygain_album_peak_str;
+            }
+            if (str != NULL) {
+                *scale = g_strtod(p + isize + 1, NULL);
+                *str = g_strndup(p + isize + 1, vsize);
+            }
+
+            /* mp3gain additional tags : 
+               the gain tag translates to scale = 2^(gain/4), 
+               i.e., in dB : 20*log(2)/log(10)*gain/4
+               -> 1.501*gain dB
+             */
+            if (uncase_strcmp(p, "MP3GAIN_UNDO") == 0) {
+                str = &file->mp3gain_undo_str;
+                scale = &file->mp3gain_undo;
+                assert(4 < vsize);  /* this tag is +left,+right */
+                *str = g_strndup(p + isize + 1, vsize);
+                *scale = 1.50515 * atoi(*str);
+            }
+            if (uncase_strcmp(p, "MP3GAIN_MINMAX") == 0) {
+                str = &file->mp3gain_minmax_str;
+                scale = &file->mp3gain_minmax;
+                *str = g_strndup(p + isize + 1, vsize);
+                assert(4 < vsize);  /* this tag is min,max */
+                *scale = 1.50515 * (atoi((*str) + 4) - atoi(*str));
+            }
+        }
+        p += isize + 1 + vsize;
+    }
+
+    free(buff);
+
+    return 0;
+}
+
+static int find_offset(VFSFile * fp)
+{
+    static const char *key = "APETAGEX";
+    char buff[20000];
+    int N = 0;
+    if (vfs_fseek(fp, -20000, SEEK_CUR) != 0);
+    if ((N = vfs_fread(buff, 1, 20000, fp)) < 16)
+        return 1;
+    int matched = 0;
+    int i, last_match = -1;
+    for (i = 0; i < N; ++i) {
+        if (buff[i] == key[matched])
+            ++matched;
+        else {
+            if (matched == 5 && buff[i] == 'P')
+                matched = 2;    // got "APET" + "AP"
+            else
+                matched = 0;
+        }
+        if (matched == 8) {
+            last_match = i;
+            matched = 0;
+        }
+    }
+    if (last_match == -1)
+        return 1;
+    return last_match + 1 - 8 + sizeof(struct APETagFooterStruct) - N;
+}
+
+void input_read_replaygain(struct mad_info_t *file)
+{
+    file->has_replaygain = FALSE;
+    file->replaygain_album_scale = -1;
+    file->replaygain_track_scale = -1;
+    file->mp3gain_undo = -77;
+    file->mp3gain_minmax = -77;
+
+
+    VFSFile *fp;
+
+    if ((fp = vfs_fopen(file->filename, "rb")) == NULL)
+        return;
+
+    if (vfs_fseek(fp, 0L, SEEK_END) != 0) {
+        vfs_fclose(fp);
+        return;
+    }
+    long pos = vfs_ftell(fp);
+    int res = -1;
+    int try = 0;
+    while (res != 0 && try < 10) {
+        // try skipping an id3 tag
+        vfs_fseek(fp, pos, SEEK_SET);
+        vfs_fseek(fp, try * -128, SEEK_CUR);
+        res = ReadAPE2Tag(fp, file);
+        ++try;
+    }
+    if (res != 0) {
+        // try brute search (don't want to parse all possible kinds of tags..)
+        vfs_fseek(fp, pos, SEEK_SET);
+        int offs = find_offset(fp);
+        if (offs <= 0) {        // found !
+            vfs_fseek(fp, pos, SEEK_SET);
+            vfs_fseek(fp, offs, SEEK_CUR);
+            res = ReadAPE2Tag(fp, file);
+            if (res != 0) {
+                g_message
+                    ("hmpf, was supposed to find a tag.. offs=%d, res=%d",
+                     offs, res);
+            }
+        }
+    }
+#ifdef DEBUG
+//#if 1
+    if (res == 0) {             // got APE tags, show the result
+        printf("RG album scale= %g, RG track scale = %g, in %s \n",
+               file->replaygain_album_scale,
+               file->replaygain_track_scale, file->filename);
+    }
+#endif
+
+    if (file->replaygain_album_scale != -1
+        || file->replaygain_track_scale != -1)
+        file->has_replaygain = TRUE;
+
+    vfs_fclose(fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/replaygain.h	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,44 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Portions derived from xmms-mad:
+ * Copyright (C) 2001-2002 Sam Clegg - See COPYING
+ * Copyright (C) 2001-2007 Samuel Krempp
+ *
+ * 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "plugin.h"
+#include "stdlib.h"
+#include "math.h"
+#include "ctype.h"
+
+#ifndef __replaygain_h__
+#define __replaygain_h__
+
+struct APETagFooterStruct
+{
+    unsigned char ID[8];
+    unsigned char Version[4];
+    unsigned char Length[4];
+    unsigned char TagCount[4];
+    unsigned char Flags[4];
+    unsigned char Reserved[8];
+};
+
+/* prototypes */
+void input_read_replaygain(struct mad_info_t *file_info);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/xing.c	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,91 @@
+/*
+ * mad plugin for audacious
+ * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa
+ *
+ * Derived from: 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <mad.h>
+
+#include "xing.h"
+
+#define XING_MAGIC	(('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
+
+/*
+ * NAME:	xing->init()
+ * DESCRIPTION:	initialize Xing structure
+ */
+void xing_init(struct xing *xing)
+{
+    xing->flags = 0;
+}
+
+/*
+ * NAME:	xing->parse()
+ * DESCRIPTION:	parse a Xing VBR header
+ */
+int xing_parse(struct xing *xing, struct mad_bitptr ptr,
+               unsigned int bitlen)
+{
+    if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
+        goto fail;
+
+    xing->flags = mad_bit_read(&ptr, 32);
+    bitlen -= 64;
+
+    if (xing->flags & XING_FRAMES) {
+        if (bitlen < 32)
+            goto fail;
+
+        xing->frames = mad_bit_read(&ptr, 32);
+        bitlen -= 32;
+    }
+
+    if (xing->flags & XING_BYTES) {
+        if (bitlen < 32)
+            goto fail;
+
+        xing->bytes = mad_bit_read(&ptr, 32);
+        bitlen -= 32;
+    }
+
+    if (xing->flags & XING_TOC) {
+        int i;
+
+        if (bitlen < 800)
+            goto fail;
+
+        for (i = 0; i < 100; ++i)
+            xing->toc[i] = mad_bit_read(&ptr, 8);
+
+        bitlen -= 800;
+    }
+
+    if (xing->flags & XING_SCALE) {
+        if (bitlen < 32)
+            goto fail;
+
+        xing->scale = mad_bit_read(&ptr, 32);
+        bitlen -= 32;
+    }
+
+    return 0;
+
+  fail:
+    xing->flags = 0;
+    return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/madplug/xing.h	Mon Feb 05 12:28:01 2007 -0800
@@ -0,0 +1,48 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef XING_H
+#define XING_H
+
+#include "mad.h"
+
+struct xing
+{
+    long flags;                 /* valid fields (see below) */
+    unsigned long frames;       /* total number of frames */
+    unsigned long bytes;        /* total number of bytes */
+    unsigned char toc[100];     /* 100-point seek table */
+    long scale;                 /* ?? */
+};
+
+enum
+{
+    XING_FRAMES = 0x00000001L,
+    XING_BYTES = 0x00000002L,
+    XING_TOC = 0x00000004L,
+    XING_SCALE = 0x00000008L
+};
+
+void xing_init(struct xing *);
+
+#define xing_finish(xing)       /* nothing */
+
+int xing_parse(struct xing *, struct mad_bitptr, unsigned int);
+
+#endif