ref: d5aabbc466238d0231e17568ad52f123eb6f2616
parent: ebeb3d0c6599523b9c292909a4ccbf31780c50bf
author: robs <robs>
date: Sun Feb 10 07:15:12 EST 2008
normalise effect
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,7 +19,7 @@
formats.
Effects: key, tempo, pad, bass, treble, new reverb, new
flanger, soft-knee companding, speed via resampling, filters
- makeover inc. gnuplot & octave plotting, splice, remix;
+ makeover inc. gnuplot & octave plotting, splice, remix, norm;
new effects chain with buffering and any # channels.
Others: soxi, open input files via URL, file merging, play
multiple files with mixed format types, play with replay-gain,
--- a/ChangeLog
+++ b/ChangeLog
@@ -15,6 +15,7 @@
o Added effect to splice together audio sections. (robs)
o Added remix effect (complements the mixer effect). (robs)
+ o Added norm (normalise) effect. (robs)
Other new features:
--- a/README
+++ b/README
@@ -85,6 +85,7 @@
o Apply an equalizer effect
o Add dithering/masking noise to a signal
o Multi-band compander
+ o Normalise the audio level
o Pad with silence
o Pan sound between channels
o Apply a phaser effect
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -560,6 +560,38 @@
sox noisy.au -n trim 0 1 noiseprof | play noisy.au noisered
.EE
.TP
+\fBnorm\fR [\fIlevel\fR]
+Normalise audio to 0dB FSD or to a given level relative to 0dB.
+Requires temporary file space to store the audio to be normalised.
+.SP
+To create a normalised copy of an audio file,
+.EX
+ sox infile outfile norm
+.EE
+can be used, though note that if `infile' has a simple encoding (e.g.
+PCM), then
+.EX
+ sox infile outfile vol \`sox infile -n stat -v 2>&1\`
+.EE
+(on systems that support this construct) might be quicker to execute
+(though perhaps not to type!) as it doesn't require a temporary file.
+.SP
+For a more complex example, suppose that `effect1' performs some unknown
+or unpredictable attenuation and that `effect2' requires up to 10dB of
+headroom, then
+.EX
+ sox infile outfile effect1 norm -10 effect2 norm
+.EE
+gives both effect2 and the output file the highest possible signal
+levels.
+.SP
+In most cases,
+.B norm \-3
+should be the maximum level used at the output file (to leave headroom
+for playback-resampling, etc.). See also the discussions of clipping
+and Replay Gain in
+.BR sox (1).
+.TP
\fBoops\fR
Out Of Phase Stereo effect.
Mixes stereo to twin-mono where each mono channel contains the
@@ -799,7 +831,7 @@
.TP
\fBrepeat \fIcount\fR
Repeat the entire audio \fIcount\fR times.
-Requires disk space to store the data to be repeated.
+Requires temporary file space to store the audio to be repeated.
Note that repeating once yields two copies: the original audio and the
repeated audio.
.TP
@@ -917,7 +949,7 @@
.TP
\fBreverse\fR
Reverse the audio completely.
-Requires disk space to store the data to be reversed.
+Requires temporary file space to store the audio to be reversed.
.TP
\fBsilence \fR[\fB\-l\fR] \fIabove-periods\fR [\fIduration
threshold\fR[\fBd\fR\^|\^\fB%\fR] [\fIbelow-periods duration
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,15 +18,15 @@
# Format with: !xargs echo|tr ' ' '\n'|sort|column|expand|sed 's/^/ /'
set(effects_srcs
- biquad echos noiseprof repeat stretch
- biquads effects noisered resample swap
- chorus fade pad reverb synth
- compand FFT pan reverse tempo
- compandt filter phaser silence tremolo
- dcshift flanger pitch skeleff trim
- dither key polyphas speed vibro
- earwax mcompand rate splice vol
- echo mixer remix stat
+ biquad echos noiseprof remix stat
+ biquads effects noisered repeat stretch
+ chorus fade normalise resample swap
+ compand FFT pad reverb synth
+ compandt filter pan reverse tempo
+ dcshift flanger phaser silence tremolo
+ dither key pitch skeleff trim
+ earwax mcompand polyphas speed vibro
+ echo mixer rate splice vol
)
set(formats_srcs
8svx cvsd hcom s1-fmt u2-fmt
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -199,7 +199,7 @@
pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c remix.c repeat.c \
resample.c reverb.c reverse.c silence.c skeleff.c speed.c \
splice.c stat.c stretch.c swap.c synth.c tempo.c tremolo.c trim.c \
- vibro.c vol.c
+ vibro.c vol.c normalise.c
libsfx_la_CFLAGS = @SAMPLERATE_CFLAGS@
libsfx_la_LIBADD = @SAMPLERATE_LIBS@ libsox.la
--- a/src/compandt.c
+++ b/src/compandt.c
@@ -19,7 +19,7 @@
#include "compandt.h"
#include <string.h>
-#define LOG_TO_LOG10(x) ((x) * 20 / log(10.))
+#define LOG_TO_LOG10(x) ((x) * 20 / M_LN10)
sox_bool sox_compandt_show(sox_compandt_t * t, sox_plot_t plot)
{
@@ -78,12 +78,12 @@
static void prepare_transfer_fn(sox_compandt_t * t)
{
int i;
- double radius = t->curve_dB * log(10.) / 20;
+ double radius = t->curve_dB * M_LN10 / 20;
for (i = 0; !i || t->segments[i-2].x; i += 2) {
t->segments[i].y += t->outgain_dB;
- t->segments[i].x *= log(10.) / 20; /* Convert to natural logs */
- t->segments[i].y *= log(10.) / 20;
+ t->segments[i].x *= M_LN10 / 20; /* Convert to natural logs */
+ t->segments[i].y *= M_LN10 / 20;
}
#define line1 t->segments[i - 4]
--- a/src/effects.h
+++ b/src/effects.h
@@ -31,6 +31,7 @@
EFFECT(mixer)
EFFECT(noiseprof)
EFFECT(noisered)
+ EFFECT(norm)
EFFECT(oops)
EFFECT(pad)
EFFECT(pan)
--- /dev/null
+++ b/src/normalise.c
@@ -1,0 +1,112 @@
+/*
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, write to the Free Software Foundation,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+/* Effect: Normalise (c) 2008 robs@users.sourceforge.net */
+
+#include "sox_i.h"
+#include <math.h>
+#include <string.h>
+
+typedef struct norm {
+ double norm0; /* Multiplier to take to 0dB FSD */
+ double level; /* Multiplier to take to 'level' */
+ sox_sample_t min, max;
+ FILE * tmp_file;
+} * priv_t;
+
+assert_static(sizeof(struct norm) <= SOX_MAX_EFFECT_PRIVSIZE,
+ /* else */ norm_PRIVSIZE_too_big);
+
+static int create(sox_effect_t * effp, int argc, char * * argv)
+{
+ priv_t p = (priv_t)effp->priv;
+ char dummy;
+
+ if (argc > 1 || (argc == 1 &&
+ (sscanf(*argv, "%lf%c", &p->level, &dummy) != 1 || p->level > 0)))
+ return sox_usage(effp);
+ p->level = dB_to_linear(p->level);
+ return SOX_SUCCESS;
+}
+
+static int start(sox_effect_t * effp)
+{
+ priv_t p = (priv_t)effp->priv;
+
+ p->norm0 = p->max = p->min = 0;
+ p->tmp_file = tmpfile();
+ if (p->tmp_file == NULL) {
+ sox_fail("can't create temporary file: %s", strerror(errno));
+ return SOX_EOF;
+ }
+ return SOX_SUCCESS;
+}
+
+static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
+ sox_sample_t * obuf, sox_size_t * isamp, sox_size_t * osamp)
+{
+ priv_t p = (priv_t)effp->priv;
+ sox_size_t len;
+
+ if (fwrite(ibuf, sizeof(*ibuf), *isamp, p->tmp_file) != *isamp) {
+ sox_fail("error writing temporary file: %s", strerror(errno));
+ return SOX_EOF;
+ }
+ for (len = *osamp; len; --len, ++ibuf) {
+ p->max = max(p->max, *ibuf);
+ p->min = min(p->min, *ibuf);
+ }
+ (void)obuf, *osamp = 0; /* samples not output until drain */
+ return SOX_SUCCESS;
+}
+
+static int drain(sox_effect_t * effp, sox_sample_t * obuf, sox_size_t * osamp)
+{
+ priv_t p = (priv_t)effp->priv;
+ sox_size_t len;
+ int result = SOX_SUCCESS;
+
+ if (!p->norm0) {
+ int shift_for_max = (4 - min(effp->outinfo.size, 4)) << 3;
+ double max = (SOX_SAMPLE_MAX >> shift_for_max) << shift_for_max;
+ 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);
+ if (len != *osamp && !feof(p->tmp_file)) {
+ sox_fail("error reading temporary file: %s", strerror(errno));
+ result = SOX_EOF;
+ }
+ for (*osamp = len; len; --len, ++obuf)
+ *obuf = floor(*obuf * p->norm0 + .5);
+ return result;
+}
+
+static int stop(sox_effect_t * effp)
+{
+ priv_t p = (priv_t)effp->priv;
+
+ fclose(p->tmp_file); /* auto-deleted by tmpfile */
+ return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const * sox_norm_effect_fn(void)
+{
+ static sox_effect_handler_t handler = {
+ "norm", "[level]", 0, create, start, flow, drain, stop, NULL
+ };
+ return &handler;
+}
--- a/src/remix.c
+++ b/src/remix.c
@@ -81,7 +81,7 @@
multiplier = sep1 == 'v' ? 1 : 0;
PARSE(sep2, "%lf", multiplier, -HUGE_VAL, separators + 4);
if (sep1 != 'v')
- multiplier = (sep1 == 'p'? 1 : -1) * exp(multiplier / 20 * log(10.));
+ multiplier = (sep1 == 'p'? 1 : -1) * dB_to_linear(multiplier);
mul_spec = sox_true;
}
if (chan2 < chan1) {int t = chan1; chan1 = chan2; chan2 = t;}
--- a/src/reverb.c
+++ b/src/reverb.c
@@ -131,7 +131,7 @@
memset(p, 0, sizeof(*p));
p->feedback = 1 - exp((reverberance - b) / (a * b));
p->hf_damping = hf_damping / 100 * .3 + .2;
- p->gain = exp(wet_gain_dB / 20 * log(10.)) * .015;
+ p->gain = dB_to_linear(wet_gain_dB) * .015;
fifo_create(&p->input_fifo, sizeof(float));
memset(fifo_write(&p->input_fifo, delay, 0), 0, delay * sizeof(float));
for (i = 0; i <= ceil(depth); ++i) {
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -253,6 +253,11 @@
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#endif
+#ifndef M_LN10
+#define M_LN10 2.30258509299404568402 /* natural log of 10 */
+#endif
+#define dB_to_linear(x) exp((x) * M_LN10 * 0.05)
+#define linear_to_dB(x) (log10(x) * 20)
typedef struct sox_globals /* Global parameters (for effects & formats) */
{
--- a/src/synth.c
+++ b/src/synth.c
@@ -511,8 +511,7 @@
* | |
* 0 p1 1
*/
-#define LOG_10_20 0.1151292546497022842009e0
- synth_out = exp(-chan->p2 * LOG_10_20 * 100); /* 0 .. 1 */
+ synth_out = dB_to_linear(chan->p2 * -100); /* 0 .. 1 */
if (phase < chan->p1)
synth_out = synth_out * exp(phase * log(1 / synth_out) / chan->p1);
else
--- a/src/vol.c
+++ b/src/vol.c
@@ -17,8 +17,6 @@
#include "sox_i.h"
#include <math.h>
-#define LOG_10_20 ((double)(0.1151292546497022842009e0))
-
typedef struct {
double gain; /* amplitude gain. */
sox_bool uselimiter;
@@ -67,7 +65,7 @@
if (!p)
return sox_usage(effp);
switch (p->value) {
- case vol_dB: vol->gain = exp(vol->gain*LOG_10_20); break;
+ case vol_dB: vol->gain = dB_to_linear(vol->gain); break;
case vol_power: /* power to amplitude, keep phase change */
vol->gain = vol->gain > 0 ? sqrt(vol->gain) : -sqrt(-vol->gain);
break;