shithub: sox

Download patch

ref: c2749992b232e8459354dd4763155b1ff3e0d228
parent: 8ab40e817a66502cc801ae5d0844583b01d0a8d6
author: robs <robs>
date: Wed Sep 10 13:14:32 EDT 2008

-b option for the norm effect; can be used to fix stereo imbalance

--- a/soxeffect.7
+++ b/soxeffect.7
@@ -804,8 +804,9 @@
 	sox noisy.au -n trim 0 1 noiseprof | play noisy.au noisered
 .EE
 .TP
-\fBnorm\fR [\fB\-i\fR] [\fIlevel\fR]
-Normalise audio to 0dB FSD or to a given level relative to 0dB.
+\fBnorm\fR [\fB\-i\fR\^|\^\fB\-b\fR] [\fIlevel\fR]
+Normalise audio to 0dB FSD, to a given level relative to 0dB, or normalise
+the balance of multi-channel audio.
 Requires temporary file space to store the audio to be normalised.
 .SP
 To create a normalised copy of an audio file,
@@ -836,6 +837,15 @@
 .B \-i
 option is given, then each channel is treated individually and
 will attain the normalised level.
+.SP
+If the
+.B \-b
+option is given (with a multi-channel audio file), then the audio
+channels will be balanced; i.e. the RMS level of each channel will be
+normalised to that of the channel with the highest RMS level.  This can
+be used, for example, to correct stereo imbalance.  Note that
+.B \-b
+can cause clipping.
 .SP
 In most cases,
 .B norm \-3
--- a/src/normalise.c
+++ b/src/normalise.c
@@ -19,9 +19,11 @@
 #include <string.h>
 
 typedef struct {
-  sox_bool      individual;
+  sox_bool      individual, balance;
   double        norm0;    /* Multiplier to take to 0dB FSD */
   double        level;    /* Multiplier to take to 'level' */
+  double        rms;
+  off_t         num_samples;
   sox_sample_t  min, max;
   FILE          * tmp_file;
 } priv_t;
@@ -30,6 +32,7 @@
 {
   priv_t * p = (priv_t *)effp->priv;
   if (argc && !strcmp(*argv, "-i")) p->individual = sox_true, ++argv, --argc;
+  if (argc && !strcmp(*argv, "-b")) p->balance = sox_true, ++argv, --argc;
   do {NUMERIC_PARAMETER(level, -100, 0)} while (0);
   p->level = dB_to_linear(p->level);
   return argc?  lsx_usage(effp) : SOX_SUCCESS;
@@ -38,7 +41,7 @@
 static int start(sox_effect_t * effp)
 {
   priv_t * p = (priv_t *)effp->priv;
-  if (!p->individual)
+  if (!(p->individual || p->balance))
     effp->flows = 1;
   p->norm0 = p->max = p->min = 0;
   p->tmp_file = tmpfile();
@@ -59,7 +62,13 @@
     sox_fail("error writing temporary file: %s", strerror(errno));
     return SOX_EOF;
   }
-  for (len = *osamp; len; --len, ++ibuf) {
+  if (p->balance) for (len = *osamp; len; --len, ++ibuf) {
+    size_t dummy = 0;
+    double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf, dummy);
+    p->rms += sqr(d);
+    ++p->num_samples;
+  }
+  else for (len = *osamp; len; --len, ++ibuf) {
     p->max = max(p->max, *ibuf);
     p->min = min(p->min, *ibuf);
   }
@@ -70,12 +79,23 @@
 static int drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * osamp)
 {
   priv_t * p = (priv_t *)effp->priv;
-  size_t len;
+  size_t len, i;
   int result = SOX_SUCCESS;
 
   if (!p->norm0) {
-    double max = lsx_sample_max(effp->out_encoding);
-    p->norm0 = p->level * min(max / p->max, (double)SOX_SAMPLE_MIN / p->min);
+    if (p->balance) {
+      double max_rms = 0, this_rms = sqrt(p->rms / p->num_samples);
+      sox_effect_t * effects = effp - effp->flow;
+      for (i = 0; i < effp->flows; ++i) {
+        priv_t * q = (priv_t *)(effects + i)->priv;
+        max_rms = max(max_rms, sqrt(q->rms / q->num_samples));
+      }
+      p->norm0 = p->level * (this_rms != 0? max_rms / this_rms : 1);
+    }
+    else {
+      double max = lsx_sample_max(effp->out_encoding);
+      p->norm0 = p->level * min(max / p->max, (double)SOX_SAMPLE_MIN / p->min);
+    }
     rewind(p->tmp_file);
   }
   len = fread(obuf, sizeof(*obuf), *osamp, p->tmp_file);
@@ -83,7 +103,9 @@
     sox_fail("error reading temporary file: %s", strerror(errno));
     result = SOX_EOF;
   }
-  for (*osamp = len; len; --len, ++obuf)
+  if (p->balance) for (*osamp = len; len; --len, ++obuf)
+    *obuf = SOX_ROUND_CLIP_COUNT(*obuf * p->norm0, effp->clips);
+  else for (*osamp = len; len; --len, ++obuf)
     *obuf = floor(*obuf * p->norm0 + .5);
   return result;
 }
@@ -97,7 +119,7 @@
 
 sox_effect_handler_t const * sox_norm_effect_fn(void)
 {
-  static sox_effect_handler_t handler = {"norm", "[-i] [level]", 0,
+  static sox_effect_handler_t handler = {"norm", "[-i|-b] [level]", 0,
     create, start, flow, drain, stop, NULL, sizeof(priv_t)};
   return &handler;
 }