changeset 4256:b0ca963fd965

adaptive scaler added, disabled hard limiter
author Eugene Zagidullin <e.asphyx@gmail.com>
date Wed, 06 Feb 2008 22:42:32 +0300
parents 70f379ff23ad
children b1f879a0bfcb
files src/audacious/main.c src/audacious/main.h src/audacious/output.c src/audacious/ui_preferences.c src/libSAD/common.h src/libSAD/dither.c src/libSAD/noicegen.c src/libSAD/noicegen.h
diffstat 8 files changed, 99 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/src/audacious/main.c	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/audacious/main.c	Wed Feb 06 22:42:32 2008 +0300
@@ -236,7 +236,7 @@
     TRUE,           /* enable clipping prevention */
     TRUE,           /* track mode */
     FALSE,          /* album mode */
-    FALSE,          /* enable hard limiter */
+    FALSE,          /* enable adaptive scaler */
     0.0,            /* preamp */
     -9.0,           /* default gain */
 };
@@ -354,7 +354,7 @@
     {"enable_clipping_prevention", &cfg.enable_clipping_prevention, TRUE},
     {"replay_gain_track",          &cfg.replay_gain_track, TRUE},
     {"replay_gain_album",          &cfg.replay_gain_album, TRUE},
-    {"enable_hard_limiter",        &cfg.enable_hard_limiter, TRUE},
+    {"enable_adaptive_scaler",     &cfg.enable_adaptive_scaler, TRUE},
 };
 
 static gint ncfgbent = G_N_ELEMENTS(bmp_boolents);
--- a/src/audacious/main.h	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/audacious/main.h	Wed Feb 06 22:42:32 2008 +0300
@@ -147,7 +147,7 @@
     gboolean enable_clipping_prevention;
     gboolean replay_gain_track;
     gboolean replay_gain_album;
-    gboolean enable_hard_limiter;
+    gboolean enable_adaptive_scaler;
     gfloat replay_gain_preamp;
     gfloat default_gain;
 };
--- a/src/audacious/output.c	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/audacious/output.c	Wed Feb 06 22:42:32 2008 +0300
@@ -495,9 +495,9 @@
     if(replay_gain_info.album_peak == 0.0 && replay_gain_info.track_peak == 0.0) {
         AUDDBG("RG info isn't set yet. Filling replay_gain_info with default values.\n");
         replay_gain_info.track_gain = cfg.default_gain;
-        replay_gain_info.track_peak = 1.0;
+        replay_gain_info.track_peak = 0.01;
         replay_gain_info.album_gain = cfg.default_gain;
-        replay_gain_info.album_peak = 1.0;
+        replay_gain_info.album_peak = 0.01;
     }
     apply_replaygain_info(&replay_gain_info);
     
@@ -779,7 +779,8 @@
     rg_enabled = cfg.enable_replay_gain;
     album_mode = cfg.replay_gain_album;
     mode.clipping_prevention = cfg.enable_clipping_prevention;
-    mode.hard_limit = cfg.enable_hard_limiter;
+    mode.hard_limit = FALSE;
+    mode.adaptive_scaler = cfg.enable_adaptive_scaler;
     
     if(!rg_enabled) return;
 
@@ -795,7 +796,7 @@
     AUDDBG("Applying Replay Gain settings:\n");
     AUDDBG("* mode:                %s\n",     mode.mode == SAD_RG_ALBUM ? "album" : "track");
     AUDDBG("* clipping prevention: %s\n",     mode.clipping_prevention ? "yes" : "no");
-    AUDDBG("* hard limit:          %s\n",     mode.hard_limit ? "yes" : "no");
+    AUDDBG("* adaptive scaler      %s\n",     mode.adaptive_scaler ? "yes" : "no");
     AUDDBG("* preamp:              %+f dB\n", mode.preamp);
     AUDDBG("Replay Gain info for current track:\n");
     AUDDBG("* track gain:          %+f dB\n", info.track_gain);
--- a/src/audacious/ui_preferences.c	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/audacious/ui_preferences.c	Wed Feb 06 22:42:32 2008 +0300
@@ -233,8 +233,10 @@
     {WIDGET_RADIO_BTN, N_("Track gain/peak"), &cfg.replay_gain_track, NULL, NULL, TRUE},
     {WIDGET_RADIO_BTN, N_("Album gain/peak"), &cfg.replay_gain_album, NULL, NULL, TRUE},
     {WIDGET_LABEL, N_("<b>Miscellaneous</b>"), NULL, NULL, NULL, TRUE},
-    {WIDGET_CHK_BTN, N_("Enable clipping prevention"), &cfg.enable_clipping_prevention, NULL, NULL, TRUE},
-    {WIDGET_CHK_BTN, N_("Enable 6 dB hard limiter"), &cfg.enable_hard_limiter, NULL, NULL, TRUE},
+    {WIDGET_CHK_BTN, N_("Enable peak info clipping prevention"), &cfg.enable_clipping_prevention, NULL,
+                     N_("Use peak value from Replay Gain info for clipping prevention"), TRUE},
+    {WIDGET_CHK_BTN, N_("Dynamically adjust scale factor to prevent clipping"), &cfg.enable_adaptive_scaler, NULL, 
+                     N_("Decrease scale factor (gain) if clipping nevertheless occurred"), TRUE},
     {WIDGET_CUSTOM, NULL, NULL, NULL, NULL, TRUE, ui_preferences_rg_params},
 };
 
@@ -1728,7 +1730,7 @@
 
     GtkWidget *spin = gtk_spin_button_new_with_range(-15, 15, 0.01);
     gtk_table_attach(GTK_TABLE(table), spin, 1, 2, 0, 1,
-                     (GtkAttachOptions) (0),
+                     (GtkAttachOptions) (GTK_FILL),
                      (GtkAttachOptions) (0), 0, 0);
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), cfg.replay_gain_preamp);
     g_signal_connect(G_OBJECT(spin), "value_changed", G_CALLBACK(on_rg_spin_changed), &cfg.replay_gain_preamp);
@@ -1747,7 +1749,7 @@
     
     spin = gtk_spin_button_new_with_range(-15, 15, 0.01);
     gtk_table_attach(GTK_TABLE(table), spin, 1, 2, 1, 2,
-                     (GtkAttachOptions) (0),
+                     (GtkAttachOptions) (GTK_FILL),
                      (GtkAttachOptions) (0), 0, 0);
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), cfg.default_gain);
     g_signal_connect(G_OBJECT(spin), "value_changed", G_CALLBACK(on_rg_spin_changed), &cfg.default_gain);
@@ -1761,6 +1763,20 @@
     
     gtk_container_add(GTK_CONTAINER(alignment), table);
     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
+    
+    GtkWidget *image = gtk_image_new_from_stock ("gtk-info", GTK_ICON_SIZE_BUTTON);
+    gtk_table_attach (GTK_TABLE (table), image, 0, 1, 2, 3,
+                      (GtkAttachOptions) (GTK_FILL),
+                      (GtkAttachOptions) (0), 0, 0);
+    
+    label = gtk_label_new (_("<span size=\"small\">Please remember that the most efficient way to prevent signal clipping is not to use "
+                             "positive values above.</span>"));
+    gtk_table_attach (GTK_TABLE (table), label, 1, 2, 2, 3,
+                      (GtkAttachOptions) (GTK_FILL),
+                      (GtkAttachOptions) (0), 0, 0);
+    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+    gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 
     return alignment;
 }
--- a/src/libSAD/common.h	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/libSAD/common.h	Wed Feb 06 22:42:32 2008 +0300
@@ -146,6 +146,7 @@
   int mode;
   int clipping_prevention;
   int hard_limit;
+  int adaptive_scaler;
   float preamp; /* in dB ! */
 } SAD_replaygain_mode;
 
--- a/src/libSAD/dither.c	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/libSAD/dither.c	Wed Feb 06 22:42:32 2008 +0300
@@ -17,8 +17,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-/* #define CLIPPING_DEBUG */
-/* #define DITHER_DEBUG */
+/* #define CLIPPING_DEBUG
+#define DEBUG
+#define DITHER_DEBUG */
 
 #include "common.h"
 #include "dither_ops.h"
@@ -51,6 +52,8 @@
 #define MAXINT(a) (1L << ((a)-1))
 #define CLIP(x,m) (x > m-1 ? m-1 : (x < -m ? -m : x))
 
+#define ADJUSTMENT_COEFFICIENT 0.1
+
 /* private object */
 typedef struct {
   SAD_sample_format input_sample_format;
@@ -66,15 +69,16 @@
   SAD_put_sample_proc put_sample;
   int dither;
   int hardlimit;
-  float scale;
-  float rg_scale;
+  double scale;
+  double rg_scale;
+  int adaptive_scaler;
 } SAD_state_priv;
 
 /* error code */
 
 //static SAD_error SAD_last_error = SAD_ERROR_OK;
 
-static inline double compute_hardlimit (double sample, float scale) {
+static inline double compute_hardlimit (double sample, double scale) {
   sample *= scale;
   const double k = 0.5;    /* -6dBFS */
   if (sample > k) {
@@ -91,8 +95,8 @@
  * samples < -1 and > 1 will be clipped
  */
 
-static inline int32_t __dither_sample_fixed_to_int (int32_t sample, int inbits, int fracbits, int outbits, float scale, int dither,
-							int hardlimit)
+static inline int32_t __dither_sample_fixed_to_int (int32_t sample, int inbits, int fracbits, int outbits, double *scale, int dither,
+							int hardlimit, int adaptive_scale)
 {
   int n_bits_to_loose, bitwidth, precision_loss;
   int32_t maxint = MAXINT(outbits);
@@ -131,18 +135,34 @@
   }
   
   assert(n_bits_to_loose >=0 );
-
+  
+  /* adaptive scaler */
+  if (adaptive_scale) {
+    int sam = sample >> n_bits_to_loose;
+    double d_sam = fabs((double)sam) / (double)(maxint - 1);
+    if (d_sam * *scale > 1.0) {
+#ifdef CLIPPING_DEBUG
+      printf("sample val %d, scale factor adjusted %f --> ", sam, *scale);
+#endif
+      *scale -= (*scale - 1.0 / d_sam) * ADJUSTMENT_COEFFICIENT;
+#ifdef CLIPPING_DEBUG
+      printf("%f\n", *scale);
+#endif
+    }
+    sample = SCALE(sample, *scale);
+  } else
+  /*****************/
   if (hardlimit) {
-    sample = (int32_t)(compute_hardlimit((double)sample/(double)MAXINT(bitwidth), scale) * (double)MAXINT(bitwidth));
+    sample = (int32_t)(compute_hardlimit((double)sample/(double)MAXINT(bitwidth), *scale) * (double)MAXINT(bitwidth));
 #ifdef PRECISION_DEBUG
     printf("Precision loss, reason: hard limiter\n", inbits, outbits);
 #endif
     precision_loss = TRUE;
   } else {
-    sample = SCALE(sample, scale);
+    sample = SCALE(sample, *scale);
   }
  
-  if (scale != 1.0){
+  if (*scale != 1.0){
     precision_loss = TRUE;
 #ifdef PRECISION_DEBUG
     printf("Precision loss, reason: scale\n", inbits, outbits);
@@ -182,18 +202,32 @@
  * Dither floating-point normalized sample to n-bits integer
  * samples < -1 and > 1 will be clipped
  */
-static inline int32_t __dither_sample_float_to_int (float sample, int nbits, float scale, int dither, int hardlimit) {
+static inline int32_t __dither_sample_float_to_int (float sample, int nbits, double *scale, int dither, int hardlimit, int adaptive_scale) {
 
 #ifdef DEEP_DEBUG
   printf("f: __dither_sample_float_to_int\n");
 #endif
 
   int32_t maxint = MAXINT(nbits);
-
+  
+  /* adaptive scaler */
+  if (adaptive_scale) {
+    if (fabs(sample) * *scale > 1.0) {
+#ifdef CLIPPING_DEBUG
+      printf("sample val %f, scale factor adjusted %f --> ", sample, *scale);
+#endif
+      *scale -= (*scale - 1.0 / sample) * ADJUSTMENT_COEFFICIENT;
+#ifdef CLIPPING_DEBUG
+      printf("%f\n", *scale);
+#endif
+    }
+    sample = SCALE(sample, *scale);
+  } else
+  /*****************/
   if (hardlimit) {
-    sample = compute_hardlimit((double)sample, scale);
+    sample = compute_hardlimit((double)sample, *scale);
   } else {
-    sample = SCALE(sample, scale);
+    sample = SCALE(sample, *scale);
   }
 
   sample *= maxint;
@@ -205,7 +239,7 @@
   val_wo_dither = CLIP(val_wo_dither, maxint);
 #endif
   if (dither) {
-    float dither_num = triangular_dither_noise_f();
+    double dither_num = triangular_dither_noise_f();
     sample += dither_num;
   }
 
@@ -227,7 +261,7 @@
   return value;
 }
 
-static inline float __dither_sample_float_to_float (float sample, float scale, int hardlimit) {
+static inline float __dither_sample_float_to_float (float sample, double scale, int hardlimit) {
 #ifdef DEEP_DEBUG
   printf("f: __dither_sample_float_to_float\n");
 #endif
@@ -239,16 +273,16 @@
   return sample;
 }
 
-static inline float __dither_sample_fixed_to_float (int32_t sample, int inbits, int fracbits, float scale, int hardlimit) {
+static inline float __dither_sample_fixed_to_float (int32_t sample, int inbits, int fracbits, double scale, int hardlimit) {
   float fsample;
 
 #ifdef DEEP_DEBUG
   printf("f: __dither_sample_fixed_to_float\n");
 #endif
   if (fracbits == 0) {
-     fsample = (float)sample / (float)MAXINT(inbits);
+     fsample = (double)sample / (double)MAXINT(inbits);
   } else {
-     fsample = (float)sample / (float)MAXINT(fracbits+1);
+     fsample = (double)sample / (double)MAXINT(fracbits+1);
   }
   return __dither_sample_float_to_float (fsample, scale, hardlimit);
 }
@@ -300,6 +334,7 @@
   priv->rg_scale = 1.0;
   priv->dither = TRUE;
   priv->hardlimit = FALSE;
+  priv->adaptive_scaler = FALSE;
 
   switch(outbuf_format->sample_format){
     case SAD_SAMPLE_S8:
@@ -393,9 +428,11 @@
   int inbits = priv->input_bits;
   int outbits = priv->output_bits;
   int fracbits = priv->input_fracbits;
-  float scale = priv->scale * priv->rg_scale;
+  double scale = priv->scale * priv->rg_scale;
+  double oldscale = scale;
   int dither = priv->dither;
   int hardlimit = priv->hardlimit;
+  int adaptive_scale = priv->adaptive_scaler;
   SAD_channels_order input_chorder = priv->input_chorder;
   SAD_channels_order output_chorder = priv->output_chorder;
 
@@ -422,7 +459,7 @@
           for(i=0; i<frames; i++) {
 	      for(ch=0; ch<channels; ch++) {
 	          float sample = GET_FLOAT_SAMPLE(inbuf, input_chorder, channels, ch ,i);
-	          int32_t isample = __dither_sample_float_to_int(sample, outbits, scale, dither, hardlimit);
+	          int32_t isample = __dither_sample_float_to_int(sample, outbits, &scale, dither, hardlimit, adaptive_scale);
                   put_sample (outbuf, isample, channels, ch, i);
 	      }
 	  }
@@ -444,19 +481,22 @@
           for(i=0; i<frames; i++) {
 	      for(ch=0; ch<channels; ch++){
   	          int32_t sample = get_sample (inbuf, channels, ch, i);
-	          int32_t isample = __dither_sample_fixed_to_int (sample, inbits, fracbits, outbits, scale, dither, hardlimit);
+	          int32_t isample = __dither_sample_fixed_to_int (sample, inbits, fracbits, outbits, &scale, dither, hardlimit, adaptive_scale);
                   put_sample (outbuf, isample, channels, ch, i);
 	      }
 	  }
       }
   }
 
+  /* recalc scale factor */
+  if (adaptive_scale && oldscale != scale) priv->rg_scale = scale / priv->scale;
+
   return SAD_ERROR_OK;
 }
 
 int SAD_dither_apply_replaygain (SAD_dither_t *state, SAD_replaygain_info *rg_info, SAD_replaygain_mode *mode) {
   SAD_state_priv *priv = (SAD_state_priv*) state;
-  float scale = -1.0, peak = 0.0;
+  double scale = -1.0, peak = 0.0;
 
   DEBUG_MSG("f: SAD_dither_apply_replaygain\n",0);
   
@@ -498,13 +538,16 @@
       if(scale * peak > 1.0) DEBUG_MSG("f: SAD_dither_apply_replaygain: clipping prevented\n",0);
 #endif
       scale = scale * peak > 1.0 ? 1.0 / peak : scale;
+      DEBUG_MSG("f: SAD_dither_apply_replaygain: new scale %f\n", scale);
     }
     scale = scale > 15.0 ? 15.0 : scale; // safety
     priv->rg_scale = scale;
     priv->hardlimit = mode->hard_limit; // apply settings
+    priv->adaptive_scaler = mode->adaptive_scaler;
   } else {
     priv->rg_scale = 1.0;
     priv->hardlimit = FALSE;
+    priv->adaptive_scaler = FALSE; // apply settings
   }
 
   return SAD_ERROR_OK;
--- a/src/libSAD/noicegen.c	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/libSAD/noicegen.c	Wed Feb 06 22:42:32 2008 +0300
@@ -35,10 +35,10 @@
     return v;
 }
 
-float triangular_dither_noise_f() {
+double triangular_dither_noise_f() {
   // Сonditionally assume we have 16 bits in fractional part
   // Please, check it thoroughly: is this assumption correct in floatin-point arithmetic?
-  return (float) triangular_dither_noise(17) / 65536.0;
+  return (double) triangular_dither_noise(17) / 65536.0;
 }
 
 void noicegen_init_rand(uint32_t seed) {
--- a/src/libSAD/noicegen.h	Tue Feb 05 00:09:07 2008 +0300
+++ b/src/libSAD/noicegen.h	Wed Feb 06 22:42:32 2008 +0300
@@ -4,7 +4,7 @@
 #include <inttypes.h>
 
 int triangular_dither_noise(int nbits);
-float triangular_dither_noise_f(void);
+double triangular_dither_noise_f(void);
 void noicegen_init_rand(uint32_t seed);
 
 #endif /*NOICEGEN_H*/