shithub: sox

Download patch

ref: a15706d1e3fa2e7e4c8588434485cbd9526707ca
parent: 8025a99ac69ba3354f478316e5c209772eea3ee0
author: robs <robs>
date: Sat Dec 13 12:43:54 EST 2008

moving some things around

--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -36,7 +36,7 @@
   compandt        filter          overdrive       reverse         tremolo
   contrast        flanger         pad             silence         trim
   dcshift         input           pan             skeleff         vol
-  delay           loudness        phaser          speed
+  delay           loudness        phaser          speed effects_i_dsp
   dither          mcompand        polyphas        splice
 )
 set(formats_srcs
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -273,7 +273,7 @@
 	polyphas.c rabbit.c rate.c rate_filters.h rate_half_fir.h \
 	rate_poly_fir0.h rate_poly_fir.h remix.c repeat.c resample.c reverb.c \
 	reverse.c silence.c skeleff.c speed.c splice.c stat.c swap.c stretch.c \
-	synth.c tempo.c tremolo.c trim.c vol.c overdrive.c
+	synth.c tempo.c tremolo.c trim.c vol.c overdrive.c effects_i_dsp.c
 if HAVE_PNG
     libsox_la_SOURCES += spectrogram.c
 endif
--- a/src/effects_i.c
+++ b/src/effects_i.c
@@ -18,12 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#ifdef NDEBUG /* Enable assert always. */
-#undef NDEBUG /* Must undef above assert.h or other that might include it. */
-#endif
-
 #include "sox_i.h"
-#include <assert.h>
 #include <string.h>
 
 #undef lsx_fail
@@ -68,56 +63,6 @@
   return a * (b / lsx_gcd(a, b));
 }
 
-/* Numerical Recipes cubic spline */
-
-void lsx_prepare_spline3(double const * x, double const * y, int n,
-    double start_1d, double end_1d, double * y_2d)
-{
-  double p, qn, sig, un, * u = lsx_malloc((n - 1) * sizeof(*u));
-  int i;
-
-  if (start_1d == HUGE_VAL)
-    y_2d[0] = u[0] = 0;      /* Start with natural spline or */
-  else {                     /* set the start first derivative */
-    y_2d[0] = -.5;
-    u[0] = (3 / (x[1] - x[0])) * ((y[1] - y[0]) / (x[1] - x[0]) - start_1d);
-  }
-
-  for (i = 1; i < n - 1; ++i) {
-    sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
-    p = sig * y_2d[i - 1] + 2;
-    y_2d[i] = (sig - 1) / p;
-    u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) -
-           (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
-    u[i] = (6 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
-  }
-  if (end_1d == HUGE_VAL)
-    qn = un = 0;             /* End with natural spline or */
-  else {                     /* set the end first derivative */
-    qn = .5;
-    un = 3 / (x[n - 1] - x[n - 2]) * (end_1d - (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]));
-  }
-  y_2d[n - 1] = (un - qn * u[n - 2]) / (qn * y_2d[n - 2] + 1);
-  for (i = n - 2; i >= 0; --i)
-    y_2d[i] = y_2d[i] * y_2d[i + 1] + u[i];
-  free(u);
-}
-
-double lsx_spline3(double const * x, double const * y, double const * y_2d,
-    int n, double x1)
-{
-  int     t, i[2] = {0, 0};
-  double  d, a, b;
-
-  for (i[1] = n - 1; i[1] - i[0] > 1; t = (i[1] + i[0]) >> 1, i[x[t] > x1] = t);
-  d = x[i[1]] - x[i[0]];
-  assert(d != 0);
-  a = (x[i[1]] - x1) / d;
-  b = (x1 - x[i[0]]) / d;
-  return a * y[i[0]] + b * y[i[1]] +
-    ((a * a * a - a) * y_2d[i[0]] + (b * b * b - b) * y_2d[i[1]]) * d * d / 6;
-}
-
 lsx_enum_item const lsx_wave_enum[] = {
   LSX_ENUM_ITEM(SOX_WAVE_,SINE)
   LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE)
@@ -270,6 +215,8 @@
 
 #if 0
 
+#include <assert.h>
+
 #define TEST(st, samp, len) \
   str = st; \
   next = lsx_parsesamples(10000, str, &samples, 't'); \
@@ -374,178 +321,6 @@
     }
   }
   return result < 0 ? -1 : result;
-}
-
-double lsx_bessel_I_0(double x)
-{
-  double term = 1, sum = 1, last_sum, x2 = x / 2;
-  int i = 1;
-  do {
-    double y = x2 / i++;
-    last_sum = sum, sum += term *= y * y;
-  } while (sum != last_sum);
-  return sum;
-}
-
-int lsx_set_dft_length(int num_taps) /* Set to 4 x nearest power of 2 */
-{
-  int result, n = num_taps;
-  for (result = 8; n > 2; result <<= 1, n >>= 1);
-  result = range_limit(result, 4096, 131072);
-  assert(num_taps * 2 < result);
-  return result;
-}
-
-#include "fft4g.h"
-int    * lsx_fft_br;
-double * lsx_fft_sc;
-static int fft_len;
-#ifdef HAVE_OPENMP
-static omp_lock_t fft_cache_lock;
-#endif
-
-static void init_fft_cache(void)
-{
-  omp_init_lock(&fft_cache_lock);
-}
-
-static void clear_fft_cache(void)
-{
-  omp_destroy_lock(&fft_cache_lock);
-  free(lsx_fft_br);
-  free(lsx_fft_sc);
-  lsx_fft_sc = NULL;
-  lsx_fft_br = NULL;
-  fft_len = 0;
-}
-
-static void update_fft_cache(int len)
-{
-  omp_set_lock(&fft_cache_lock);
-  if (len > fft_len) {
-    int old_n = fft_len;
-    fft_len = len;
-    lsx_fft_br = lsx_realloc(lsx_fft_br, dft_br_len(fft_len) * sizeof(*lsx_fft_br));
-    lsx_fft_sc = lsx_realloc(lsx_fft_sc, dft_sc_len(fft_len) * sizeof(*lsx_fft_sc));
-    if (!old_n)
-      lsx_fft_br[0] = 0;
-  }
-}
-
-static sox_bool is_power_of_2(int x)
-{
-  return !(x < 2 || (x & (x - 1)));
-}
-
-void lsx_safe_rdft(int len, int type, double * d)
-{
-  assert(is_power_of_2(len));
-  update_fft_cache(len);
-  lsx_rdft(len, type, d, lsx_fft_br, lsx_fft_sc);
-  omp_unset_lock(&fft_cache_lock);
-}
-
-void lsx_safe_cdft(int len, int type, double * d)
-{
-  assert(is_power_of_2(len));
-  update_fft_cache(len);
-  lsx_cdft(len, type, d, lsx_fft_br, lsx_fft_sc);
-  omp_unset_lock(&fft_cache_lock);
-}
-
-void lsx_power_spectrum(int n, double const * in, double * out)
-{
-  int i;
-  double * work = lsx_memdup(in, n * sizeof(*work));
-  lsx_safe_rdft(n, 1, work);
-  out[0] = sqr(work[0]);
-  for (i = 2; i < n; i += 2)
-    out[i >> 1] = sqr(work[i]) + sqr(work[i + 1]);
-  out[i >> 1] = sqr(work[1]);
-  free(work);
-}
-
-void lsx_power_spectrum_f(int n, float const * in, float * out)
-{
-  int i;
-  double * work = lsx_malloc(n * sizeof(*work));
-  for (i = 0; i< n; ++i) work[i] = in[i];
-  lsx_safe_rdft(n, 1, work);
-  out[0] = sqr(work[0]);
-  for (i = 2; i < n; i += 2)
-    out[i >> 1] = sqr(work[i]) + sqr(work[i + 1]);
-  out[i >> 1] = sqr(work[1]);
-  free(work);
-}
-
-void lsx_apply_hann_f(float h[], const int num_points)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    double x = 2 * M_PI * i / m;
-    h[i] *= .5 - .5 * cos(x);
-  }
-}
-
-void lsx_apply_hann(double h[], const int num_points)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    double x = 2 * M_PI * i / m;
-    h[i] *= .5 - .5 * cos(x);
-  }
-}
-
-void lsx_apply_hamming(double h[], const int num_points)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    double x = 2 * M_PI * i / m;
-    h[i] *= .53836 - .46164 * cos(x);
-  }
-}
-
-void lsx_apply_bartlett(double h[], const int num_points)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    h[i] *= 2. / m * (m / 2. - fabs(i - m / 2.));
-  }
-}
-
-void lsx_apply_blackman(double h[], const int num_points, double alpha /*.16*/)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    double x = 2 * M_PI * i / m;
-    h[i] *= (1 - alpha) *.5 - .5 * cos(x) + alpha * .5 * cos(2 * x);
-  }
-}
-
-void lsx_apply_blackman_nutall(double h[], const int num_points)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i < num_points; ++i) {
-    double x = 2 * M_PI * i / m;
-    h[i] *= .3635819 - .4891775 * cos(x) + .1365995 * cos(2 * x) - .0106411 * cos(3 * x);
-  }
-}
-
-double lsx_kaiser_beta(double att)
-{
-  if (att > 100  ) return .1117 * att - 1.11;
-  if (att > 50   ) return .1102 * (att - 8.7);
-  if (att > 20.96) return .58417 * pow(att -20.96, .4) + .07886 * (att - 20.96);
-  return 0;
-}
-
-void lsx_apply_kaiser(double h[], const int num_points, double beta)
-{
-  int i, m = num_points - 1;
-  for (i = 0; i <= m; ++i) {
-    double x = 2. * i / m - 1;
-    h[i] *= lsx_bessel_I_0(beta * sqrt(1 - x * x)) / lsx_bessel_I_0(beta);
-  }
 }
 
 int lsx_effects_init(void)
--- /dev/null
+++ b/src/effects_i_dsp.c
@@ -1,0 +1,359 @@
+/* libSoX internal DSP functions.
+ * All public functions & data are prefixed with lsx_ .
+ *
+ * Copyright (c) 2008 robs@users.sourceforge.net
+ *
+ * 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 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,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef NDEBUG /* Enable assert always. */
+#undef NDEBUG /* Must undef above assert.h or other that might include it. */
+#endif
+
+#include "sox_i.h"
+#include <assert.h>
+#include <string.h>
+
+/* Numerical Recipes cubic spline */
+
+void lsx_prepare_spline3(double const * x, double const * y, int n,
+    double start_1d, double end_1d, double * y_2d)
+{
+  double p, qn, sig, un, * u = lsx_malloc((n - 1) * sizeof(*u));
+  int i;
+
+  if (start_1d == HUGE_VAL)
+    y_2d[0] = u[0] = 0;      /* Start with natural spline or */
+  else {                     /* set the start first derivative */
+    y_2d[0] = -.5;
+    u[0] = (3 / (x[1] - x[0])) * ((y[1] - y[0]) / (x[1] - x[0]) - start_1d);
+  }
+
+  for (i = 1; i < n - 1; ++i) {
+    sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
+    p = sig * y_2d[i - 1] + 2;
+    y_2d[i] = (sig - 1) / p;
+    u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) -
+           (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
+    u[i] = (6 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
+  }
+  if (end_1d == HUGE_VAL)
+    qn = un = 0;             /* End with natural spline or */
+  else {                     /* set the end first derivative */
+    qn = .5;
+    un = 3 / (x[n - 1] - x[n - 2]) * (end_1d - (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]));
+  }
+  y_2d[n - 1] = (un - qn * u[n - 2]) / (qn * y_2d[n - 2] + 1);
+  for (i = n - 2; i >= 0; --i)
+    y_2d[i] = y_2d[i] * y_2d[i + 1] + u[i];
+  free(u);
+}
+
+double lsx_spline3(double const * x, double const * y, double const * y_2d,
+    int n, double x1)
+{
+  int     t, i[2] = {0, 0};
+  double  d, a, b;
+
+  for (i[1] = n - 1; i[1] - i[0] > 1; t = (i[1] + i[0]) >> 1, i[x[t] > x1] = t);
+  d = x[i[1]] - x[i[0]];
+  assert(d != 0);
+  a = (x[i[1]] - x1) / d;
+  b = (x1 - x[i[0]]) / d;
+  return a * y[i[0]] + b * y[i[1]] +
+    ((a * a * a - a) * y_2d[i[0]] + (b * b * b - b) * y_2d[i[1]]) * d * d / 6;
+}
+
+double lsx_bessel_I_0(double x)
+{
+  double term = 1, sum = 1, last_sum, x2 = x / 2;
+  int i = 1;
+  do {
+    double y = x2 / i++;
+    last_sum = sum, sum += term *= y * y;
+  } while (sum != last_sum);
+  return sum;
+}
+
+int lsx_set_dft_length(int num_taps) /* Set to 4 x nearest power of 2 */
+{
+  int result, n = num_taps;
+  for (result = 8; n > 2; result <<= 1, n >>= 1);
+  result = range_limit(result, 4096, 131072);
+  assert(num_taps * 2 < result);
+  return result;
+}
+
+#include "fft4g.h"
+int    * lsx_fft_br;
+double * lsx_fft_sc;
+static int fft_len;
+#ifdef HAVE_OPENMP
+static omp_lock_t fft_cache_lock;
+#endif
+
+void init_fft_cache(void)
+{
+  omp_init_lock(&fft_cache_lock);
+}
+
+void clear_fft_cache(void)
+{
+  omp_destroy_lock(&fft_cache_lock);
+  free(lsx_fft_br);
+  free(lsx_fft_sc);
+  lsx_fft_sc = NULL;
+  lsx_fft_br = NULL;
+  fft_len = 0;
+}
+
+static void update_fft_cache(int len)
+{
+  omp_set_lock(&fft_cache_lock);
+  if (len > fft_len) {
+    int old_n = fft_len;
+    fft_len = len;
+    lsx_fft_br = lsx_realloc(lsx_fft_br, dft_br_len(fft_len) * sizeof(*lsx_fft_br));
+    lsx_fft_sc = lsx_realloc(lsx_fft_sc, dft_sc_len(fft_len) * sizeof(*lsx_fft_sc));
+    if (!old_n)
+      lsx_fft_br[0] = 0;
+  }
+}
+
+static sox_bool is_power_of_2(int x)
+{
+  return !(x < 2 || (x & (x - 1)));
+}
+
+void lsx_safe_rdft(int len, int type, double * d)
+{
+  assert(is_power_of_2(len));
+  update_fft_cache(len);
+  lsx_rdft(len, type, d, lsx_fft_br, lsx_fft_sc);
+  omp_unset_lock(&fft_cache_lock);
+}
+
+void lsx_safe_cdft(int len, int type, double * d)
+{
+  assert(is_power_of_2(len));
+  update_fft_cache(len);
+  lsx_cdft(len, type, d, lsx_fft_br, lsx_fft_sc);
+  omp_unset_lock(&fft_cache_lock);
+}
+
+void lsx_power_spectrum(int n, double const * in, double * out)
+{
+  int i;
+  double * work = lsx_memdup(in, n * sizeof(*work));
+  lsx_safe_rdft(n, 1, work);
+  out[0] = sqr(work[0]);
+  for (i = 2; i < n; i += 2)
+    out[i >> 1] = sqr(work[i]) + sqr(work[i + 1]);
+  out[i >> 1] = sqr(work[1]);
+  free(work);
+}
+
+void lsx_power_spectrum_f(int n, float const * in, float * out)
+{
+  int i;
+  double * work = lsx_malloc(n * sizeof(*work));
+  for (i = 0; i< n; ++i) work[i] = in[i];
+  lsx_safe_rdft(n, 1, work);
+  out[0] = sqr(work[0]);
+  for (i = 2; i < n; i += 2)
+    out[i >> 1] = sqr(work[i]) + sqr(work[i + 1]);
+  out[i >> 1] = sqr(work[1]);
+  free(work);
+}
+
+void lsx_apply_hann_f(float h[], const int num_points)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    double x = 2 * M_PI * i / m;
+    h[i] *= .5 - .5 * cos(x);
+  }
+}
+
+void lsx_apply_hann(double h[], const int num_points)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    double x = 2 * M_PI * i / m;
+    h[i] *= .5 - .5 * cos(x);
+  }
+}
+
+void lsx_apply_hamming(double h[], const int num_points)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    double x = 2 * M_PI * i / m;
+    h[i] *= .53836 - .46164 * cos(x);
+  }
+}
+
+void lsx_apply_bartlett(double h[], const int num_points)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    h[i] *= 2. / m * (m / 2. - fabs(i - m / 2.));
+  }
+}
+
+void lsx_apply_blackman(double h[], const int num_points, double alpha /*.16*/)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    double x = 2 * M_PI * i / m;
+    h[i] *= (1 - alpha) *.5 - .5 * cos(x) + alpha * .5 * cos(2 * x);
+  }
+}
+
+void lsx_apply_blackman_nutall(double h[], const int num_points)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i < num_points; ++i) {
+    double x = 2 * M_PI * i / m;
+    h[i] *= .3635819 - .4891775 * cos(x) + .1365995 * cos(2 * x) - .0106411 * cos(3 * x);
+  }
+}
+
+double lsx_kaiser_beta(double att)
+{
+  if (att > 100  ) return .1117 * att - 1.11;
+  if (att > 50   ) return .1102 * (att - 8.7);
+  if (att > 20.96) return .58417 * pow(att -20.96, .4) + .07886 * (att - 20.96);
+  return 0;
+}
+
+void lsx_apply_kaiser(double h[], const int num_points, double beta)
+{
+  int i, m = num_points - 1;
+  for (i = 0; i <= m; ++i) {
+    double x = 2. * i / m - 1;
+    h[i] *= lsx_bessel_I_0(beta * sqrt(1 - x * x)) / lsx_bessel_I_0(beta);
+  }
+}
+
+double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale)
+{
+  double * h = malloc(num_taps * sizeof(*h)), sum = 0;
+  int i, m = num_taps - 1;
+  assert(Fc >= 0 && Fc <= 1);
+  for (i = 0; i <= m / 2; ++i) {
+    double x = M_PI * (i - .5 * m), y = 2. * i / m - 1;
+    h[i] = x? sin(Fc * x) / x : Fc;
+    sum += h[i] *= lsx_bessel_I_0(beta * sqrt(1 - y * y));
+    if (m - i != i)
+      sum += h[m - i] = h[i];
+  }
+  for (i = 0; i < num_taps; ++i) h[i] *= scale / sum;
+  return h;
+}
+
+double * lsx_design_lpf(
+    double Fp,      /* End of pass-band; ~= 0.01dB point */
+    double Fc,      /* Start of stop-band */
+    double Fn,      /* Nyquist freq; e.g. 0.5, 1, PI */
+    sox_bool allow_aliasing,
+    double att,     /* Stop-band attenuation in dB */
+    int * num_taps, /* (Single phase.)  0: value will be estimated */
+    int k)          /* Number of phases; 0 for single-phase */
+{
+  double tr_bw, beta;
+
+  if (allow_aliasing)
+    Fc += (Fc - Fp) * LSX_TO_3dB;
+  Fp /= Fn, Fc /= Fn;        /* Normalise to Fn = 1 */
+  tr_bw = LSX_TO_6dB * (Fc - Fp); /* Transition band-width: 6dB to stop points */
+
+  if (*num_taps == 0) {        /* TODO this could be cleaner, esp. for k != 0 */
+    double n160 = (.0425* att - 1.4) /  tr_bw;    /* Half order for att = 160 */
+    int n = n160 * (16.556 / (att - 39.6) + .8625) + .5;  /* For att [80,160) */
+    *num_taps = k? 2 * n : 2 * (n + (n & 1)) + 1; /* =1 %4 (0 phase 1/2 band) */
+  }
+  assert(att >= 80);
+  beta = att < 100 ? .1102 * (att - 8.7) : .1117 * att - 1.11;
+  if (k)
+    *num_taps = *num_taps * k - 1;
+  else k = 1;
+  return lsx_make_lpf(*num_taps, (Fc - tr_bw) / k, beta, (double)k);
+}
+
+void lsx_fir_to_phase(double * * h, int * len,
+    int * post_len, double phase0)
+{
+  double * work, phase = (phase0 > 50 ? 100 - phase0 : phase0) / 50;
+  int work_len, begin, end, peak = 0, i = *len;
+
+  for (work_len = 32; i > 1; work_len <<= 1, i >>= 1);
+  work = calloc(work_len, sizeof(*work));
+  for (i = 0; i < *len; ++i) work[i] = (*h)[i];
+
+  lsx_safe_rdft(work_len, 1, work); /* Cepstral: */
+  work[0] = log(fabs(work[0])), work[1] = log(fabs(work[1]));
+  for (i = 2; i < work_len; i += 2) {
+    work[i] = log(sqrt(sqr(work[i]) + sqr(work[i + 1])));
+    work[i + 1] = 0;
+  }
+  lsx_safe_rdft(work_len, -1, work);
+  for (i = 0; i < work_len; ++i) work[i] *= 2. / work_len;
+  for (i = 1; i < work_len / 2; ++i) { /* Window to reject acausal components */
+    work[i] *= 2;
+    work[i + work_len / 2] = 0;
+  }
+  lsx_safe_rdft(work_len, 1, work);
+
+  /* Some filters require phase unwrapping at this point.  Ours give dis-
+   * continuities only in the stop band, so no need to unwrap in this case. */
+
+  for (i = 2; i < work_len; i += 2) /* Interpolate between linear & min phase */
+    work[i + 1] = phase * M_PI * .5 * i + (1 - phase) * work[i + 1];
+
+  work[0] = exp(work[0]), work[1] = exp(work[1]);
+  for (i = 2; i < work_len; i += 2) {
+    double x = exp(work[i]);
+    work[i    ] = x * cos(work[i + 1]);
+    work[i + 1] = x * sin(work[i + 1]);
+  }
+  lsx_safe_rdft(work_len, -1, work);
+  for (i = 0; i < work_len; ++i) work[i] *= 2. / work_len;
+
+  for (i = 1; i < work_len; ++i) if (work[i] > work[peak])  /* Find peak pos. */
+    peak = i;                                           /* N.B. peak val. > 0 */
+
+  if (phase == 0)
+    begin = 0;
+  else if (phase == 1)
+    begin = 1 + (work_len - *len) / 2;
+  else {
+    if (peak < work_len / 4) { /* Low phases can wrap impulse, so unwrap: */
+      memmove(work + work_len / 4, work, work_len / 2 * sizeof(*work));
+      memmove(work, work + work_len * 3 / 4, work_len / 4 * sizeof(*work));
+      peak += work_len / 4;
+    }
+    begin = (.997 - (2 - phase) * .22) * *len + .5;
+    end   = (.997 + (0 - phase) * .22) * *len + .5;
+    begin = peak - begin - (begin & 1);
+    end   = peak + 1 + end + (end & 1);
+    *len = end - begin;
+    *h = realloc(*h, *len * sizeof(**h));
+  }
+  for (i = 0; i < *len; ++i)
+    (*h)[i] = work[begin + (phase0 > 50 ? *len - 1 - i : i)];
+  *post_len = phase0 > 50 ? peak - begin : begin + *len - (peak + 1);
+  free(work);
+}
--- a/src/loudness.c
+++ b/src/loudness.c
@@ -47,7 +47,7 @@
     NUMERIC_PARAMETER(n    ,127 ,2047)
   } while (0);
   p->n = 2 * p->n + 1;
-  return argc?  lsx_usage(effp) : SOX_SUCCESS;
+  return argc? lsx_usage(effp) : SOX_SUCCESS;
 }
 
 static double * make_filter(int n, double start, double delta, double rate)
--- a/src/rate.c
+++ b/src/rate.c
@@ -187,123 +187,6 @@
   }
 }
 
-static double * make_lpf(int num_taps, double Fc, double beta, double scale)
-{
-  double * h = malloc(num_taps * sizeof(*h)), sum = 0;
-  int i, m = num_taps - 1;
-  assert(Fc >= 0 && Fc <= 1);
-  for (i = 0; i <= m / 2; ++i) {
-    double x = M_PI * (i - .5 * m), y = 2. * i / m - 1;
-    h[i] = x? sin(Fc * x) / x : Fc;
-    sum += h[i] *= lsx_bessel_I_0(beta * sqrt(1 - y * y));
-    if (m - i != i)
-      sum += h[m - i] = h[i];
-  }
-  for (i = 0; i < num_taps; ++i) h[i] *= scale / sum;
-  return h;
-}
-
-#define TO_6dB .5869
-#define TO_3dB ((2/3.) * (.5 + TO_6dB))
-#define MAX_TBW0 36.
-#define MAX_TBW0A (MAX_TBW0 / (1 + TO_3dB))
-#define MAX_TBW3 floor(MAX_TBW0 * TO_3dB)
-#define MAX_TBW3A floor(MAX_TBW0A * TO_3dB)
-
-static double * design_lpf(
-    double Fp,      /* End of pass-band; ~= 0.01dB point */
-    double Fc,      /* Start of stop-band */
-    double Fn,      /* Nyquist freq; e.g. 0.5, 1, PI */
-    sox_bool allow_aliasing,
-    double att,     /* Stop-band attenuation in dB */
-    int * num_taps, /* (Single phase.)  0: value will be estimated */
-    int k)          /* Number of phases; 0 for single-phase */
-{
-  double tr_bw, beta;
-
-  if (allow_aliasing)
-    Fc += (Fc - Fp) * TO_3dB;
-  Fp /= Fn, Fc /= Fn;        /* Normalise to Fn = 1 */
-  tr_bw = TO_6dB * (Fc - Fp); /* Transition band-width: 6dB to stop points */
-
-  if (*num_taps == 0) {        /* TODO this could be cleaner, esp. for k != 0 */
-    double n160 = (.0425* att - 1.4) /  tr_bw;    /* Half order for att = 160 */
-    int n = n160 * (16.556 / (att - 39.6) + .8625) + .5;  /* For att [80,160) */
-    *num_taps = k? 2 * n : 2 * (n + (n & 1)) + 1; /* =1 %4 (0 phase 1/2 band) */
-  }
-  assert(att >= 80);
-  beta = att < 100 ? .1102 * (att - 8.7) : .1117 * att - 1.11;
-  if (k)
-    *num_taps = *num_taps * k - 1;
-  else k = 1;
-  return make_lpf(*num_taps, (Fc - tr_bw) / k, beta, (double)k);
-}
-
-static void fir_to_phase(double * * h, int * len,
-    int * post_len, double phase0)
-{
-  double * work, phase = (phase0 > 50 ? 100 - phase0 : phase0) / 50;
-  int work_len, begin, end, peak = 0, i = *len;
-
-  for (work_len = 32; i > 1; work_len <<= 1, i >>= 1);
-  work = calloc(work_len, sizeof(*work));
-  for (i = 0; i < *len; ++i) work[i] = (*h)[i];
-
-  lsx_safe_rdft(work_len, 1, work); /* Cepstral: */
-  work[0] = log(fabs(work[0])), work[1] = log(fabs(work[1]));
-  for (i = 2; i < work_len; i += 2) {
-    work[i] = log(sqrt(sqr(work[i]) + sqr(work[i + 1])));
-    work[i + 1] = 0;
-  }
-  lsx_safe_rdft(work_len, -1, work);
-  for (i = 0; i < work_len; ++i) work[i] *= 2. / work_len;
-  for (i = 1; i < work_len / 2; ++i) { /* Window to reject acausal components */
-    work[i] *= 2;
-    work[i + work_len / 2] = 0;
-  }
-  lsx_safe_rdft(work_len, 1, work);
-
-  /* Some filters require phase unwrapping at this point.  Ours give dis-
-   * continuities only in the stop band, so no need to unwrap in this case. */
-
-  for (i = 2; i < work_len; i += 2) /* Interpolate between linear & min phase */
-    work[i + 1] = phase * M_PI * .5 * i + (1 - phase) * work[i + 1];
-
-  work[0] = exp(work[0]), work[1] = exp(work[1]);
-  for (i = 2; i < work_len; i += 2) {
-    double x = exp(work[i]);
-    work[i    ] = x * cos(work[i + 1]);
-    work[i + 1] = x * sin(work[i + 1]);
-  }
-  lsx_safe_rdft(work_len, -1, work);
-  for (i = 0; i < work_len; ++i) work[i] *= 2. / work_len;
-
-  for (i = 1; i < work_len; ++i) if (work[i] > work[peak])  /* Find peak pos. */
-    peak = i;                                           /* N.B. peak val. > 0 */
-
-  if (phase == 0)
-    begin = 0;
-  else if (phase == 1)
-    begin = 1 + (work_len - *len) / 2;
-  else {
-    if (peak < work_len / 4) { /* Low phases can wrap impulse, so unwrap: */
-      memmove(work + work_len / 4, work, work_len / 2 * sizeof(*work));
-      memmove(work, work + work_len * 3 / 4, work_len / 4 * sizeof(*work));
-      peak += work_len / 4;
-    }
-    begin = (.997 - (2 - phase) * .22) * *len + .5;
-    end   = (.997 + (0 - phase) * .22) * *len + .5;
-    begin = peak - begin - (begin & 1);
-    end   = peak + 1 + end + (end & 1);
-    *len = end - begin;
-    *h = realloc(*h, *len * sizeof(**h));
-  }
-  for (i = 0; i < *len; ++i)
-    (*h)[i] = work[begin + (phase0 > 50 ? *len - 1 - i : i)];
-  *post_len = phase0 > 50 ? peak - begin : begin + *len - (peak + 1);
-  free(work);
-}
-
 static void half_band_filter_init(rate_shared_t * p, unsigned which,
     int num_taps, sample_t const h[], double Fp, double atten, int multiplier,
     double phase, sox_bool allow_aliasing)
@@ -324,10 +207,10 @@
   else {
     /* Adjustment to negate att degradation with intermediate phase */
     double att = phase && phase != 50 && phase != 100? atten * (34./33) : atten;
-    double * h = design_lpf(Fp, 1., 2., allow_aliasing, att, &num_taps, 0);
+    double * h = lsx_design_lpf(Fp, 1., 2., allow_aliasing, att, &num_taps, 0);
 
     if (phase != 50)
-      fir_to_phase(&h, &num_taps, &f->post_peak, phase);
+      lsx_fir_to_phase(&h, &num_taps, &f->post_peak, phase);
     else f->post_peak = num_taps / 2;
 
     dft_length = lsx_set_dft_length(num_taps);
@@ -422,8 +305,8 @@
     f1 = &f->interp[interp_order];
     if (!last_stage.shared->poly_fir_coefs) {
       int num_taps = 0, phases = divisor == 1? (1 << f1->phase_bits) : divisor;
-      raw_coef_t * coefs =
-          design_lpf(f->pass, f->stop, 1., sox_false, f->att, &num_taps, phases);
+      raw_coef_t * coefs = lsx_design_lpf(
+          f->pass, f->stop, 1., sox_false, f->att, &num_taps, phases);
       assert(num_taps == f->num_coefs * phases - 1);
       last_stage.shared->poly_fir_coefs =
           prepare_coefs(coefs, f->num_coefs, phases, interp_order, mult);
@@ -445,8 +328,8 @@
       {0, NULL, .931, 110}, {0, NULL, .931, 125}, {0, NULL, .931, 170}};
     filter_t const * f = &filters[quality - Low];
     double att = allow_aliasing? (34./33)* f->a : f->a; /* negate att degrade */
-    double bw = bandwidth? 1 - (1 - bandwidth / 100) / TO_3dB : f->bw;
-    double min = 1 - (allow_aliasing? MAX_TBW0A : MAX_TBW0) / 100;
+    double bw = bandwidth? 1 - (1 - bandwidth / 100) / LSX_TO_3dB : f->bw;
+    double min = 1 - (allow_aliasing? LSX_MAX_TBW0A : LSX_MAX_TBW0) / 100;
     assert((size_t)(quality - Low) < array_length(filters));
     half_band_filter_init(shared, p->upsample, f->len, f->h, bw, att, mult, phase, allow_aliasing);
     if (p->upsample) {
@@ -573,7 +456,7 @@
   while ((c = getopt(argc, argv, opts)) != -1) switch (c) {
     GETOPT_NUMERIC('i', coef_interp, 1 , 3)
     GETOPT_NUMERIC('p', phase,  0 , 100)
-    GETOPT_NUMERIC('b', bandwidth,  100 - MAX_TBW3, 99.7)
+    GETOPT_NUMERIC('b', bandwidth,  100 - LSX_MAX_TBW3, 99.7)
     case 'M': p->phase =  0; break;
     case 'I': p->phase = 25; break;
     case 'L': p->phase = 50; break;
@@ -589,8 +472,8 @@
     return SOX_EOF;
   }
 
-  if (p->bandwidth && p->bandwidth < 100 - MAX_TBW3A && p->allow_aliasing) {
-    lsx_fail("minimum allowed bandwidth with aliasing is %g%%", 100 - MAX_TBW3A);
+  if (p->bandwidth && p->bandwidth < 100 - LSX_MAX_TBW3A && p->allow_aliasing) {
+    lsx_fail("minimum allowed bandwidth with aliasing is %g%%", 100 - LSX_MAX_TBW3A);
     return SOX_EOF;
   }
 
--- a/src/sox.c
+++ b/src/sox.c
@@ -1627,6 +1627,7 @@
 "--replay-gain track|album|off  Default: off (sox, rec), track (play)",
 "-R                       Use default random numbers (same on each run of SoX)",
 "-S, --show-progress      Display progress while processing audio data",
+"--single-threaded        Disable parallel effects channels processing",
 "--temp DIRECTORY         Specify the directory to use for temporary files",
 "--version                Display version number of SoX and exit",
 "-V[LEVEL]                Increment or set verbosity level (default 2); levels:",
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -85,6 +85,8 @@
 int lsx_set_dft_length(int num_taps);
 extern int * lsx_fft_br;
 extern double * lsx_fft_sc;
+void init_fft_cache(void);
+void clear_fft_cache(void);
 void lsx_safe_rdft(int len, int type, double * d);
 void lsx_safe_cdft(int len, int type, double * d);
 void lsx_power_spectrum(int n, double const * in, double * out);
@@ -97,6 +99,23 @@
 void lsx_apply_blackman_nutall(double h[], const int num_points);
 double lsx_kaiser_beta(double att);
 void lsx_apply_kaiser(double h[], const int num_points, double beta);
+double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale);
+double * lsx_design_lpf(
+    double Fp,      /* End of pass-band; ~= 0.01dB point */
+    double Fc,      /* Start of stop-band */
+    double Fn,      /* Nyquist freq; e.g. 0.5, 1, PI */
+    sox_bool allow_aliasing,
+    double att,     /* Stop-band attenuation in dB */
+    int * num_taps, /* (Single phase.)  0: value will be estimated */
+    int k);         /* Number of phases; 0 for single-phase */
+void lsx_fir_to_phase(double * * h, int * len,
+    int * post_len, double phase0);
+#define LSX_TO_6dB .5869
+#define LSX_TO_3dB ((2/3.) * (.5 + LSX_TO_6dB))
+#define LSX_MAX_TBW0 36.
+#define LSX_MAX_TBW0A (LSX_MAX_TBW0 / (1 + LSX_TO_3dB))
+#define LSX_MAX_TBW3 floor(LSX_MAX_TBW0 * LSX_TO_3dB)
+#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB)
 
 #ifndef HAVE_STRCASECMP
 int strcasecmp(const char *s1, const char *s2);