diff src/libSAD/dither.c @ 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 63eb5966f105
children bb0638143fc8
line wrap: on
line diff
--- 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;