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;
}