ref: 30d829e27b4123315444185a5a02b409101a74e6
parent: 71897cdf178d224b4750764284e1b2aaaea112e7
author: robs <robs>
date: Sun Nov 2 07:58:56 EST 2008
new dither
--- a/AUTHORS
+++ b/AUTHORS
@@ -17,8 +17,8 @@
EFFECTS: tempo, pad, bass, bend, treble, delay, new reverb, new
flanger, soft-knee companding, speed via resampling, filters
makeover inc. gnuplot & octave plotting, splice, remix, norm,
- contrast, new rate, spectrogram, new pitch, riaa, loudness;
- new effects chain with buffering and any # channels.
+ contrast, new rate, spectrogram, new pitch, riaa, loudness, new
+ dither, new effects chain with buffering and any # channels.
OTHERS: open input files via URL, file merge, play
multiple files with mixed format types, play with replay-gain,
building with cmake, much manual improvement and expansion,
--- a/ChangeLog
+++ b/ChangeLog
@@ -49,6 +49,7 @@
add -s -L options to be compatible with SoX v14.1.0. (robs)
o New options for `rate' effect to configure phase response,
band-width and aliasing. (robs)
+ o New options for 'dither' effect: RPDF, TPDF, noise-shaping. (robs)
o New `riaa' effect: RIAA vinyl playback EQ. (robs)
o New `loudness' effect: gain control with ISO 226 loudness
compensation. (robs)
--- a/sox.1
+++ b/sox.1
@@ -1491,12 +1491,21 @@
delay 0 .27 .54 .76 1.01 1.3 remix - fade h 0.1 2.72 2.5
.EE
.TP
-\fBdither\fR [\fIdepth\fR]
+\fBdither\fR [\fB\-r\fR|\fB\-t\fR] [\fB\-s\fR] [\fIdepth\fR]
Apply dithering to the audio.
-Dithering deliberately adds TPDF white noise to the signal
+Dithering deliberately adds a small amount of noise to the signal
in order to mask audible quantization effects that
can occur if the output sample size is less than 24 bits.
-By default, the amount of noise added is \(12 bit;
+The
+.B \-r
+option selects Rectangular Probability Density Function (RPDF) white noise;
+the default (or with the
+.B \-t
+option) is Triangular (TPDF) white noise.
+Noise-shaping for CDDA (44100Hz) can be selected with
+.BR \-s .
+By default, the amount of noise added is \(+-\(12 bit for RPDF, \(+-1 bit
+for TPDF;
the optional \fIdepth\fR parameter is a (linear or voltage)
multiplier of this amount.
.SP
--- a/src/dither.c
+++ b/src/dither.c
@@ -1,34 +1,98 @@
-/* libSoX dithering noise effect file. July 5, 1991
+/* Effect: dither/noise-shape Copyright (c) 2008 robs@users.sourceforge.net
*
- * Copyright (c) 1999-8 Chris Bagwell & SoX contributors
- * Copyright 1991 Lance Norskog And Sundry Contributors
+ * 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.1 of the License, or (at
+ * your option) any later version.
*
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
+ * 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.
*
- * TODO: does triangular noise, could do local shaping.
+ * 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,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "sox_i.h"
+#include "getopt.h"
-typedef struct {double amount;} priv_t;
+#define N 9
+#define CONVOLVE _ _ _ _ _ _ _ _ _
+static double coefs[][N] = {
+ {0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {2.033, -2.165, 1.959, -1.590, .6149, 0, 0, 0, 0},
+ {2.412, -3.370, 3.937, -4.174, 3.353, -2.205, 1.281, -.569, .0847},
+ {1.662, -1.263, .4827, -.2913, .1268, -.1124, .03252, -.01265, -.03524},
+ {2.847, -4.685, 6.214, -7.184, 6.639, -5.032, 3.263, -1.632, .4191},
+};
+#define PREC effp->out_signal.precision
+
+#define RAND_CONST 140359821
+static int32_t rand_ = ~RAND_CONST;
+#define RAND_ (rand_ = rand_ * RAND_CONST + 1)
+
+typedef enum {Pdf_rectangular, Pdf_triangular, Pdf_gaussian} pdf_type_t;
+static lsx_enum_item const pdf_types[] = {
+ LSX_ENUM_ITEM(Pdf_,rectangular)
+ LSX_ENUM_ITEM(Pdf_,triangular)
+ LSX_ENUM_ITEM(Pdf_,gaussian)
+ {0, 0}};
+
+typedef enum {Shape_none, Shape_lipshitz, Shape_f_weighted,
+ Shape_modified_e_weighted, Shape_improved_e_weighted} filter_type_t;
+static lsx_enum_item const filter_types[] = {
+ LSX_ENUM_ITEM(Shape_,none)
+ LSX_ENUM_ITEM(Shape_,lipshitz)
+ {"f-weighted", Shape_f_weighted},
+ {"modified-e-weighted", Shape_modified_e_weighted},
+ {"improved-e-weighted", Shape_improved_e_weighted},
+ {0, 0}};
+
+typedef struct {
+ pdf_type_t pdf;
+ filter_type_t filter;
+ double am0, am1, depth;
+ double previous_errors[N * 2];
+ size_t pos;
+} priv_t;
+
static int getopts(sox_effect_t * effp, int argc, char * * argv)
{
priv_t * p = (priv_t *)effp->priv;
- p->amount = M_SQRT2; /* Default to half a bit. */
- do {NUMERIC_PARAMETER(amount, 1, 10)} while (0);
- return argc? lsx_usage(effp) : SOX_SUCCESS;
+ int c;
+
+ p->pdf = Pdf_triangular;
+ while ((c = getopt(argc, argv, "+rtsf:")) != -1) switch (c) {
+ case 'r': p->pdf = Pdf_rectangular; break;
+ case 't': p->pdf = Pdf_triangular ; break;
+ case 's': p->filter = Shape_improved_e_weighted; break;
+ case 'f': p->filter = lsx_enum_option(c, filter_types); break;
+ default: lsx_fail("invalid option `-%c'", optopt); return lsx_usage(effp);
+ }
+ argc -= optind, argv += optind;
+ p->depth = 1;
+ do {NUMERIC_PARAMETER(depth, .5, 4)} while (0);
+ return argc? lsx_usage(effp) : SOX_SUCCESS;
}
static int start(sox_effect_t * effp)
{
priv_t * p = (priv_t *)effp->priv;
- if (effp->out_signal.precision > 16)
- return SOX_EFF_NULL; /* Dithering not needed at >= 24 bits */
- p->amount *= 1 << (16 - effp->out_signal.precision);
+
+ if (PREC >= 24)
+ return SOX_EFF_NULL; /* Dithering not needed at this resolution */
+
+ if (p->filter &&
+ (effp->in_signal.rate < 44100 || effp->in_signal.rate > 48000)) {
+ lsx_fail("noise shaping not supported with this sample rate");
+ return SOX_EOF;
+ }
+ p->am1 = p->depth / (1 << PREC);
+ p->am0 = (p->pdf == Pdf_triangular) * p->am1;
+ lsx_report("pdf=%s filter=%s depth=%g", lsx_find_enum_value(p->pdf, pdf_types)->text, lsx_find_enum_value(p->filter, filter_types)->text, p->depth);
return SOX_SUCCESS;
}
@@ -37,12 +101,23 @@
{
priv_t * p = (priv_t *)effp->priv;
size_t len = *isamp = *osamp = min(*isamp, *osamp);
+ int dummy = 0;
- while (len--) { /* 16 signed bits of triangular noise: */
- int tri16 = ((rand() % 32768) + (rand() % 32768)) - 32767;
- double l = *ibuf++ + tri16 * p->amount;
- *obuf++ = SOX_ROUND_CLIP_COUNT(l, effp->clips);
+ if (p->filter) while (len--) {
+ #define _ d += coefs[p->filter][j] * p->previous_errors[p->pos + j], ++j;
+ double r = p->am0 * RAND_ + p->am1 * RAND_;
+ double error, d = *ibuf++;
+ int j = 0;
+ CONVOLVE
+ *obuf = SOX_ROUND_CLIP_COUNT(d + r, dummy);
+ error = d - ((((*obuf++^(1<<31))+(1<<(31-PREC)))&(-1<<(32-PREC)))^(1<<31));
+ p->pos = p->pos? p->pos - 1 : p->pos - 1 + N;
+ p->previous_errors[p->pos + N] = p->previous_errors[p->pos] = error;
}
+ else while (len--) {
+ double d = *ibuf++ + p->am0 * RAND_ + p->am1 * RAND_;
+ *obuf++ = SOX_ROUND_CLIP_COUNT(d, dummy);
+ }
return SOX_SUCCESS;
}
@@ -49,8 +124,8 @@
sox_effect_handler_t const * sox_dither_effect_fn(void)
{
static sox_effect_handler_t handler = {
- "dither", "[amount]", SOX_EFF_MCHAN | SOX_EFF_PREC,
- getopts, start, flow, 0, 0, 0, sizeof(priv_t)
+ "dither", "[-r|-t] [-s] [depth]",
+ SOX_EFF_GETOPT | SOX_EFF_PREC, getopts, start, flow, 0, 0, 0, sizeof(priv_t)
};
return &handler;
}
--- a/src/sox.h
+++ b/src/sox.h
@@ -554,6 +554,7 @@
lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * lsx_enum_items);
lsx_enum_item const * lsx_find_enum_value(unsigned value, lsx_enum_item const * lsx_enum_items);
+int lsx_enum_option(int c, lsx_enum_item const * items);
char const * lsx_find_file_extension(char const * pathname);
char const * lsx_sigfigs3(size_t number);
char const * lsx_sigfigs3p(double percentage);
--- a/src/spectrogram.c
+++ b/src/spectrogram.c
@@ -72,24 +72,6 @@
#define secs(cols) \
((double)(cols) * p->step_size * p->block_steps / effp->in_signal.rate)
-static int enum_option(int c, lsx_enum_item const * items)
-{
- lsx_enum_item const * p = lsx_find_enum_text(optarg, items);
- if (p == NULL) {
- size_t len = 1;
- char * set = lsx_malloc(len);
- *set = 0;
- for (p = items; p->text; ++p) {
- set = lsx_realloc(set, len += 2 + strlen(p->text));
- strcat(set, ", "); strcat(set, p->text);
- }
- lsx_fail("-%c: '%s' is not one of: %s.", c, optarg, set + 2);
- free(set);
- return INT_MAX;
- }
- return p->value;
-}
-
static int getopts(sox_effect_t * effp, int argc, char **argv)
{
priv_t * p = (priv_t *)effp->priv;
@@ -108,7 +90,7 @@
GETOPT_NUMERIC('Z', gain ,-100, 100)
GETOPT_NUMERIC('q', spectrum_points, 0 , p->spectrum_points)
GETOPT_NUMERIC('p', perm , 1 , 6)
- case 'w': p->win_type = enum_option(c, window_options); break;
+ case 'w': p->win_type = lsx_enum_option(c, window_options); break;
case 's': p->slack_overlap = sox_true; break;
case 't': p->title = optarg; break;
case 'c': p->comment = optarg; break;
@@ -117,7 +99,7 @@
case 'l': p->light_background = sox_true; break;
case 'h': p->high_colour = sox_true; break;
case 'o': p->out_name = optarg; break;
- default: lsx_fail("unknown option `-%c'", optopt); return lsx_usage(effp);
+ default: lsx_fail("invalid option `-%c'", optopt); return lsx_usage(effp);
}
p->gain = -p->gain;
--p->y_size, --p->perm;
--- a/src/util.c
+++ b/src/util.c
@@ -17,6 +17,7 @@
*/
#include "sox_i.h"
+#include "getopt.h"
#include <ctype.h>
#include <stdio.h>
@@ -74,6 +75,24 @@
if (value == enum_items->value)
return enum_items;
return NULL;
+}
+
+int lsx_enum_option(int c, lsx_enum_item const * items)
+{
+ lsx_enum_item const * p = lsx_find_enum_text(optarg, items);
+ if (p == NULL) {
+ size_t len = 1;
+ char * set = lsx_malloc(len);
+ *set = 0;
+ for (p = items; p->text; ++p) {
+ set = lsx_realloc(set, len += 2 + strlen(p->text));
+ strcat(set, ", "); strcat(set, p->text);
+ }
+ lsx_fail("-%c: '%s' is not one of: %s.", c, optarg, set + 2);
+ free(set);
+ return INT_MAX;
+ }
+ return p->value;
}
char const * lsx_sigfigs3(size_t number)