shithub: sox

Download patch

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)