--- xmms-mad-0.8/src/configure.c	2004-10-30 10:20:05.000000000 +0000
+++ xmms-mad-0.8-sam/src/configure.c	2005-03-06 11:30:31.000000000 +0000
@@ -21,16 +21,19 @@
 #include "xmms-mad.h"
 
 #include <gtk/gtk.h>
+#include <math.h>
 #include <xmms/configfile.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 void
 configure_win_ok (GtkWidget *widget, gpointer data)
 {
   ConfigFile *cfg;
+  const gchar *text=NULL;
 
 #ifdef DEBUG
   g_messadge("saving\n");
@@ -39,6 +42,18 @@
   xmmsmad_config.fast_play_time_calc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fast_playback));
   xmmsmad_config.use_xing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_xing));
   xmmsmad_config.dither = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dither));
+
+  xmmsmad_config.replaygain.enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_enable));
+  xmmsmad_config.replaygain.track_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_track_mode));
+  xmmsmad_config.hard_limit = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hard_limit));
+  text = gtk_entry_get_text (GTK_ENTRY (RG_default));
+  xmmsmad_config.replaygain.default_db = g_strdup(text);
+
+  text = gtk_entry_get_text (GTK_ENTRY (pregain));
+  xmmsmad_config.pregain_db = g_strdup(text);
+
+  xmmsmad_config_compute(&xmmsmad_config);
+
   cfg = xmms_cfg_open_default_file();
   if (!cfg)
     cfg = xmms_cfg_new ();
@@ -46,6 +61,12 @@
   xmms_cfg_write_boolean(cfg, "MAD", "fast_play_time_calc", xmmsmad_config.fast_play_time_calc);
   xmms_cfg_write_boolean(cfg, "MAD", "use_xing", xmmsmad_config.use_xing);
   xmms_cfg_write_boolean(cfg, "MAD", "dither", xmmsmad_config.dither);
+  xmms_cfg_write_boolean(cfg, "MAD", "hard_limit", xmmsmad_config.hard_limit);
+  xmms_cfg_write_string(cfg, "MAD", "pregain_db", xmmsmad_config.pregain_db);
+
+  xmms_cfg_write_boolean(cfg, "MAD", "RG.enable", xmmsmad_config.replaygain.enable);
+  xmms_cfg_write_boolean(cfg, "MAD", "RG.track_mode", xmmsmad_config.replaygain.track_mode);
+  xmms_cfg_write_string(cfg, "MAD", "RG.default_db", xmmsmad_config.replaygain.default_db);
   xmms_cfg_write_default_file (cfg);
   xmms_cfg_free (cfg);
   gtk_widget_destroy (configure_win);
@@ -60,6 +81,7 @@
 xmmsmad_configure (void)
 {
   GtkWidget *bbox, *ok, *cancel;
+  GtkWidget *label, *RG_default_hbox, *pregain_hbox;
 
   if (configure_win != NULL)
     {
@@ -81,7 +103,7 @@
 
   fast_playback = gtk_check_button_new_with_label("Use fast playtime calculation");
   gtk_box_pack_start(GTK_BOX(vbox), fast_playback, TRUE, TRUE, 0);
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_playback),
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_playback), 
       xmmsmad_config.fast_play_time_calc);
 
   use_xing = gtk_check_button_new_with_label("Parse XING headers");
@@ -92,6 +114,40 @@
   gtk_box_pack_start(GTK_BOX(vbox), dither, TRUE, TRUE, 0);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dither), xmmsmad_config.dither);
 
+  /* SKR added config : */
+  RG_enable = gtk_check_button_new_with_label("Enable replaygain");
+  gtk_box_pack_start(GTK_BOX(vbox), RG_enable, TRUE, TRUE, 0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_enable),
+                               xmmsmad_config.replaygain.enable);
+  RG_track_mode = gtk_check_button_new_with_label("Prefer TRACK replaygain");
+  gtk_box_pack_start(GTK_BOX(vbox), RG_track_mode, TRUE, TRUE, 0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_track_mode),
+                               xmmsmad_config.replaygain.track_mode);
+
+  hard_limit = gtk_check_button_new_with_label("hard-limit samples (prevent clipping)");
+  gtk_box_pack_start(GTK_BOX(vbox), hard_limit, TRUE, TRUE, 0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hard_limit),
+                               xmmsmad_config.hard_limit);
+
+  label = gtk_label_new("gain to use if no RG tag (dB):");
+  RG_default_hbox = gtk_hbox_new(FALSE, 5);
+  gtk_box_pack_start(GTK_BOX(vbox), 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), xmmsmad_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("Pre-gain (dB):");
+  pregain_hbox = gtk_hbox_new(FALSE, 5);
+  gtk_box_pack_start(GTK_BOX(vbox), pregain_hbox, TRUE, TRUE, 0);
+  pregain = gtk_entry_new ();
+  gtk_widget_set_usize(pregain, 80, -1);
+  gtk_entry_set_text (GTK_ENTRY(pregain), xmmsmad_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);
+
+
   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);
@@ -104,7 +160,7 @@
   gtk_widget_grab_default(ok);
 
   cancel = gtk_button_new_with_label("Cancel");
-  gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked",
+  gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", 
       GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(configure_win));
   GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
   gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
--- xmms-mad-0.8/src/decoder.c	2005-02-10 14:06:23.000000000 +0000
+++ xmms-mad-0.8-sam/src/decoder.c	2005-03-06 11:36:46.000000000 +0000
@@ -18,6 +18,7 @@
  *
  * $superduper: decoder.c,v 1.12 2005/02/10 14:06:23 sam Exp $
  */
+#include <math.h>
 #include <assert.h>
 #include <pthread.h>
 #include <signal.h>
@@ -35,8 +36,42 @@
  * Scale PCM data
  */
 static inline signed int
-scale (mad_fixed_t sample)
+scale (mad_fixed_t sample, struct mad_info_t* file_info)
 {
+  /* replayGain by SamKR */
+  gdouble scale = -1;
+  if(xmmsmad_config.replaygain.enable) {
+    if(file_info->has_replaygain) {
+      scale = file_info->replaygain_track_scale;
+      if(file_info->replaygain_album_scale != -1 
+         && (scale==-1 || ! xmmsmad_config.replaygain.track_mode) ) {
+        scale = file_info->replaygain_album_scale;
+      }
+    } 
+    if(scale==-1)
+      scale = xmmsmad_config.replaygain.default_scale; 
+  }
+  if(scale==-1)
+    scale = 1.0;
+  if(xmmsmad_config.pregain_scale != 1)
+    scale = scale * xmmsmad_config.pregain_scale;
+  
+  /* hard-limit (clipping-prevention) */
+  if(xmmsmad_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 */
@@ -107,13 +142,13 @@
     {
       signed int sample;
       /* output sample(s) in 16-bit signed little-endian PCM */
-      sample = scale (*left_ch++);
+      sample = scale (*left_ch++, info);
       output[pos++] = (sample >> 0) & 0xff;
       output[pos++] = (sample >> 8) & 0xff;
 
       if (MAD_NCHANNELS (header) == 2)
 	{
-	  sample = scale (*right_ch++);
+	  sample = scale (*right_ch++, info);
 	  output[pos++] = (sample >> 0) & 0xff;
 	  output[pos++] = (sample >> 8) & 0xff;
 	}
--- xmms-mad-0.8/src/fileinfo.c	2003-09-24 18:01:50.000000000 +0000
+++ xmms-mad-0.8-sam/src/fileinfo.c	2005-03-06 02:55:42.000000000 +0000
@@ -27,6 +27,7 @@
 #include "xmms-mad.h"
 #include "input.h"
 
+#include <math.h> // log10
 #include <xmms/util.h>
 #include <gtk/gtk.h>
 
@@ -39,7 +40,8 @@
 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;
+static GtkWidget *mpeg_fileinfo, *mpeg_replaygain, *mpeg_replaygain2,
+  *mpeg_replaygain3, *mpeg_replaygain4, *mp3gain1, *mp3gain2;
 #endif /* !NOGUI */
 
 static GList *genre_list = 0;
@@ -337,11 +339,37 @@
   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);
 
+
   gtk_widget_show_all(window);
 }
 
@@ -420,8 +448,60 @@
   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;
--- xmms-mad-0.8/src/input.c	2005-03-06 12:02:43.000000000 +0000
+++ xmms-mad-0.8-sam/src/input.c	2005-03-06 11:19:51.000000000 +0000
@@ -57,6 +57,8 @@
 
 #include "input.h"
 
+#include "read_replaygain.h"
+
 #define DIR_SEPARATOR '/'
 #define HEADER_SIZE 256
 #define LINE_LENGTH 256
@@ -358,6 +360,13 @@
   info->sdata         = streamdata_new ();
   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;
+
   input_parse_url (info);
 
   if (!info->remote)
@@ -438,6 +447,7 @@
   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);
+  title_input->date = input_id3_get_string (info->tag, ID3_FRAME_YEAR);
   string = input_id3_get_string (info->tag, ID3_FRAME_TRACK);
   if (string) 
     {
@@ -445,12 +455,28 @@
       g_free (string);
     }
 
+  string = input_id3_get_string(info->tag, ID3_FRAME_YEAR);
+  if (string) 
+    {
+      title_input->year = atoi (string);
+      g_free (string);
+    }
+  title_input->file_name = g_strdup(g_basename(info->filename));
+  title_input->file_path = g_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 (xmms_get_gentitle_format (), title_input);
   if (title_input->track_name)   g_free (title_input->track_name);
   if (title_input->performer)    g_free (title_input->performer);
   if (title_input->album_name)   g_free (title_input->album_name);
   if (title_input->genre)        g_free (title_input->genre);
   if (title_input->comment)      g_free (title_input->comment);
+  if (title_input->date)         g_free (title_input->date);
+  if (title_input->file_name)    g_free (title_input->file_name);
+  if (title_input->file_path)    g_free (title_input->file_path);
   g_free (title_input);
 }
 
@@ -474,6 +500,7 @@
   else
     { /* local mp3 file */
       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)
 	{
@@ -667,6 +694,13 @@
   if (info->sdata   ) streamdata_free (info->sdata);
   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);
+
   /* set everything to zero in case it gets used again. */
   memset(info, 0, sizeof(struct mad_info_t));
 #ifdef DEBUG
--- xmms-mad-0.8/src/read_replaygain.h	1970-01-01 00:00:00.000000000 +0000
+++ xmms-mad-0.8-sam/src/read_replaygain.h	2005-03-06 02:55:16.000000000 +0000
@@ -0,0 +1,230 @@
+#include "xmms-mad.h"
+#include "stdlib.h"
+#include "math.h"
+#include "ctype.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];
+};
+
+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);
+}
+
+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;
+}
+
+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
+
+int ReadAPE2Tag ( FILE* fp, struct mad_info_t * file_info)
+{
+    unsigned long               vsize;
+    unsigned long               isize;
+    unsigned long               flags;
+    unsigned char*              buff;
+    unsigned char*              p;
+    unsigned char*              end;
+    struct APETagFooterStruct   T;
+    unsigned long               TagLen;
+    unsigned long               TagCount;
+
+    if ( fseek ( fp, -sizeof T, SEEK_CUR ) != 0 )
+        return 18;
+    if ( fread ( &T, 1, sizeof T, fp ) != sizeof T )
+        return 2;
+    if ( memcmp ( T.ID, "APETAGEX", sizeof T.ID ) != 0 )
+        return 3;
+    if ( Read_LE_Uint32 (T.Version) != 2000 )
+        return 4;
+    TagLen = Read_LE_Uint32 (T.Length);
+    if ( TagLen < sizeof T )
+        return 5;
+    if ( fseek ( fp, - TagLen, SEEK_CUR ) != 0 )
+        return 6;
+    if ( (buff = (unsigned char *)malloc ( TagLen )) == NULL ) {
+        return 7;
+    }
+    if ( fread ( buff, 1, TagLen - sizeof T, fp ) != TagLen - sizeof T ) {
+        free ( buff );
+        return 8;
+    }
+
+    TagCount = Read_LE_Uint32 (T.TagCount);
+    end = buff + TagLen - sizeof (T);
+    for ( p = buff; p < end && TagCount--; ) {
+        vsize = Read_LE_Uint32 ( p ); p += 4;
+        flags = Read_LE_Uint32 ( 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_info->replaygain_album_scale;
+            str = & file_info->replaygain_album_str;
+          }
+          if(uncase_strcmp(p, "REPLAYGAIN_TRACK_GAIN") ==0) {
+            scale = & file_info->replaygain_track_scale;
+            str = & file_info->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_info->replaygain_track_peak;
+            str = & file_info->replaygain_track_peak_str;
+          }
+          if(uncase_strcmp(p, "REPLAYGAIN_ALBUM_PEAK") ==0) {
+            scale = & file_info->replaygain_album_peak;
+            str = & file_info->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_info->mp3gain_undo_str;
+            scale = & file_info->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_info->mp3gain_minmax_str;
+            scale = & file_info->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;
+}
+
+int find_offset(FILE* fp) {
+  static const char* key = "APETAGEX";
+  char buff[20000];
+  int N=0;
+  if ( fseek ( fp, -20000, SEEK_CUR ) != 0 )
+    ;
+  if ( (N=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_info) {
+  file_info->has_replaygain = FALSE;
+  file_info->replaygain_album_scale = -1;
+  file_info->replaygain_track_scale = -1;
+  file_info->mp3gain_undo = -77;
+  file_info->mp3gain_minmax = -77;
+
+
+    FILE*   fp;
+
+    if ( (fp = fopen ( file_info->filename, "rb" )) == NULL )
+        return;
+
+    if ( fseek ( fp, 0L, SEEK_END ) != 0 ) {
+        fclose (fp);
+        return ;
+    }
+    long pos = ftell (fp);
+    int res = -1;
+    int try = 0;
+    while(res!=0 && try < 10) {
+      // try skipping an id3 tag
+      fseek(fp, pos, SEEK_SET);
+      fseek(fp, try * -128, SEEK_CUR);
+      res = ReadAPE2Tag( fp, file_info);
+      ++try;
+    }
+    if(res!=0) {
+      // try brute search (don't want to parse all possible kinds of tags..)
+      fseek(fp, pos, SEEK_SET);
+      int offs = find_offset(fp);
+      if(offs<=0) { // found !
+        fseek(fp, pos, SEEK_SET);
+        fseek(fp, offs, SEEK_CUR);
+        res = ReadAPE2Tag( fp, file_info);
+        if(res != 0) {
+          g_message("hmpf, was supposed to find a tag.. offs=%d, res=%d",
+                    offs, res);
+        }
+      }
+    }
+      
+
+#ifdef DEBUG
+    if(res==0) { // got APE tags, show the result
+      printf("RG album scale= %g, RG track scale = %g, in %s \n\n", 
+             file_info->replaygain_album_scale,
+             file_info->replaygain_track_scale,
+             file_info->filename);
+    }
+#endif
+
+    if(file_info->replaygain_album_scale != -1 || file_info->replaygain_track_scale  != -1)
+      file_info -> has_replaygain = TRUE;
+
+    fclose (fp);
+}
--- xmms-mad-0.8/src/xmms-mad.c	2005-03-06 12:01:33.000000000 +0000
+++ xmms-mad-0.8-sam/src/xmms-mad.c	2005-03-06 03:04:38.000000000 +0000
@@ -16,12 +16,14 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * $Id: xmms-mad.c,v 1.15 2005/02/10 14:06:23 sam Exp $
+ * $Id: xmms-mad.c,v 1.5 2005/03/06 03:04:38 sam Exp $
  */
 #include "config.h"
 #include "xmms-mad.h"
 #include "input.h"
 
+#include <math.h> // pow
+
 #include <pthread.h>
 #include <gtk/gtk.h>
 #include <xmms/util.h>
@@ -48,13 +50,42 @@
 static GtkWidget *error_dialog = 0;
 #endif /* !NOGUI */
 
+void xmmsmad_config_compute(struct xmmsmad_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
 xmmsmad_init ()
 {
   ConfigFile *cfg;
-  xmmsmad_config.http_buffer_size = HTTP_BUFFER_SIZE,
+  xmmsmad_config.http_buffer_size = HTTP_BUFFER_SIZE;
   xmmsmad_config.fast_play_time_calc = TRUE;
   xmmsmad_config.use_xing = TRUE;
+  xmmsmad_config.dither = TRUE;
+  xmmsmad_config.pregain_db = "+0.00";
+  xmmsmad_config.replaygain.enable = TRUE;
+  xmmsmad_config.replaygain.track_mode = FALSE;
+  xmmsmad_config.hard_limit = FALSE;
+  xmmsmad_config.replaygain.default_db = "-9.00";
+
   cfg = xmms_cfg_open_default_file();
   if (cfg)
     {
@@ -62,7 +93,13 @@
       xmms_cfg_read_boolean(cfg, "MAD", "fast_play_time_calc", &xmmsmad_config.fast_play_time_calc);
       xmms_cfg_read_boolean(cfg, "MAD", "use_xing", &xmmsmad_config.use_xing);
       xmms_cfg_read_boolean(cfg, "MAD", "dither", &xmmsmad_config.dither);
+      xmms_cfg_read_boolean(cfg, "MAD", "hard_limit", &xmmsmad_config.hard_limit);
+      xmms_cfg_read_string(cfg, "MAD", "pregain_db", &xmmsmad_config.pregain_db);
+      xmms_cfg_read_boolean(cfg, "MAD", "RG.enable", &xmmsmad_config.replaygain.enable);
+      xmms_cfg_read_boolean(cfg, "MAD", "RG.track_mode", &xmmsmad_config.replaygain.track_mode);
+      xmms_cfg_read_string(cfg, "MAD", "RG.default_db", &xmmsmad_config.replaygain.default_db);
     }
+  xmmsmad_config_compute(&xmmsmad_config);
 }
 
 static void
@@ -93,6 +130,13 @@
     }
   else
     {
+      /* I've seen some flac files beginning with id3 frames.. 
+         so let's exclude known non-mp3 filename extensions */
+      if (strcasecmp ("flac", filename + strlen (filename) - 3) == 0 ||
+          strcasecmp ("mpc", filename + strlen (filename) - 3) == 0 )
+        {
+          rtn = 0;
+        }
       fin = open (filename, O_RDONLY);
       if (fin >= 0 && read (fin, check, 4) == 4)
 	{
@@ -287,7 +331,7 @@
       gtk_container_border_width (GTK_CONTAINER(hbox1), 5);
       gtk_widget_realize (about_window);
 
-      label1 = gtk_label_new (DESCRIPTION " v" VERSION " by Sam Clegg\nhttp://www.superduper.net/xmms-mad/\nBased on libmad v" MAD_VERSION " by Rob Leslie\nhttp://mad.sourceforge.net");
+      label1 = gtk_label_new (DESCRIPTION " v" VERSION " by Sam Clegg\nhttp://www.superduper.net/xmms-mad/\nWith SKR mods (v1.20)\nhttp://www.crans.org/~krempp/xmms-mad/\nBased on libmad v" MAD_VERSION " by Rob Leslie\nhttp://mad.sourceforge.net");
       gtk_object_set_data (GTK_OBJECT (about_window), "label1", label1);
       gtk_widget_show (label1);
       gtk_box_pack_start (GTK_BOX (hbox1), label1, TRUE, TRUE, 0);
--- xmms-mad-0.8/src/xmms-mad.h	2005-02-10 14:06:23.000000000 +0000
+++ xmms-mad-0.8-sam/src/xmms-mad.h	2005-03-06 02:11:30.000000000 +0000
@@ -60,6 +60,21 @@
   struct id3_file *id3file;
   struct xing xing;
 
+  /* 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;
@@ -76,8 +91,20 @@
   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;
 };
 
+void xmmsmad_config_compute(struct xmmsmad_config_t * config);
+// compute scale values from "_db" strings
+
 extern void *decode (void *arg);
 extern void xmmsmad_error (gchar *fmt, ...);
 extern void xmmsmad_configure ();
