shithub: sox

Download patch

ref: dce8fe1d141dd3912143feecdaebcd87374ef8e3
parent: 002ad34d50dbcbf7f410921d0a3f7aa14da4a02b
author: Rob Sykes <robs@users.sourceforge.net>
date: Tue Apr 17 15:41:10 EDT 2012

rate speed-ups: down-sample by 3/4, down-sample by > 4

--- a/src/effects_i_dsp.c
+++ b/src/effects_i_dsp.c
@@ -271,9 +271,10 @@
   return h;
 }
 
-int lsx_lpf_num_taps(double att, double tr_bw, int k)
+void lsx_kaiser_params(double att, double tr_bw, double * beta, int * num_taps)
 {                    /* TODO this could be cleaner, esp. for k != 0 */
   int n;
+  *beta = *beta < 0? lsx_kaiser_beta(att) : *beta;
   if (att <= 80)
     n = .25 / M_PI * (att - 7.95) / (2.285 * tr_bw) + .5;
   else {
@@ -280,33 +281,30 @@
     double n160 = (.0425* att - 1.4) / tr_bw;   /* Half order for att = 160 */
     n = n160 * (16.556 / (att - 39.6) + .8625) + .5;  /* For att [80,160) */
   }
-  return k? 2 * n * k - 1 : 2 * (n + (n & 1)) + 1; /* =1 %4 (0 phase 1/2 band) */
+  *num_taps = *num_taps? *num_taps : 2 * n;
 }
 
 double * lsx_design_lpf(
     double Fp,      /* End of pass-band; ~= 0.01dB point */
-    double Fc,      /* Start of stop-band */
+    double Fs,      /* 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 beta)
+    int * num_taps, /* 0: value will be estimated */
+    int k,          /* >0: number of phases; <0: num_taps ≡ 1 (mod -k) */
+    double beta)    /* <0: value will be estimated */
 {
   double tr_bw;
-
+  int n = *num_taps, phases = max(k, 1), modulo = max(-k, 1);
   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 (beta < 0)
-    beta = lsx_kaiser_beta(att);
-  if (!*num_taps)
-    *num_taps = lsx_lpf_num_taps(att, tr_bw, k);
-  k = max(k, 1);
-  lsx_debug("design_lpf Fp=%g tr_bw=%g Fc=%g", Fp, tr_bw, Fc);
-  return lsx_make_lpf(*num_taps, (Fc - tr_bw) / k, beta, (double)k, sox_true);
+    Fs += (Fs - Fp) * LSX_TO_3dB;
+  Fp /= Fn, Fs /= Fn;        /* Normalise to Fn = 1 */
+  tr_bw = LSX_TO_6dB * (Fs - Fp); /* Transition band-width: 6dB to stop points */
+  tr_bw /= phases, Fs /= phases;
+  lsx_kaiser_params(att, tr_bw, &beta, num_taps);
+  if (!n)
+    *num_taps = phases > 1? *num_taps / phases * phases + phases - 1 : (*num_taps + modulo - 2) / modulo * modulo + 1;
+  return lsx_make_lpf(*num_taps, Fs - tr_bw, beta, (double)phases, sox_true);
 }
 
 static double safe_log(double x)
--- a/src/rate.c
+++ b/src/rate.c
@@ -160,7 +160,7 @@
       lsx_safe_rdft(f->dft_length, 1, output);
     }
     output[0] *= f->coefs[0];
-    if (p->step.parts.integer) {
+    if (p->step.parts.integer > 0) {
       output[1] *= f->coefs[1];
       for (i = 2; i < f->dft_length; i += 2) {
         tmp = output[i];
@@ -177,25 +177,26 @@
       else fifo_trim_by(output_fifo, overlap);
     }
     else { /* F-domain */
-      for (i = 2; i < (f->dft_length >> 1); i += 2) {
+      int m = -p->step.parts.integer;
+      for (i = 2; i < (f->dft_length >> m); i += 2) {
         tmp = output[i];
         output[i  ] = f->coefs[i  ] * tmp - f->coefs[i+1] * output[i+1];
         output[i+1] = f->coefs[i+1] * tmp + f->coefs[i  ] * output[i+1];
       }
       output[1] = f->coefs[i] * output[i] - f->coefs[i+1] * output[i+1];
-      lsx_safe_rdft(f->dft_length >> 1, -1, output);
-      fifo_trim_by(output_fifo, (f->dft_length + overlap) >> 1);
+      lsx_safe_rdft(f->dft_length >> m, -1, output);
+      fifo_trim_by(output_fifo, (((1 << m) - 1) * f->dft_length + overlap) >> m);
     }
   }
 }
 
-static void setup_dft_stage(rate_shared_t * shared, int which, stage_t * stage, int L, int M)
+static void setup_dft_stage(rate_shared_t * shared, int which, stage_t * stage, int L, int M, sox_bool allow_aliasing)
 {
   stage->fn = dft_stage_fn;
   stage->preload = shared->dft_filter[which].post_peak / L;
   stage->remL    = shared->dft_filter[which].post_peak % L;
   stage->L = L;
-  stage->step.parts.integer = M;
+  stage->step.parts.integer = abs(3-M) == 1 && !allow_aliasing? -M/2 : M;
   stage->which = which;
 }
 
@@ -217,20 +218,12 @@
     f->post_peak = num_taps / 2;
   }
   else {
-    double * h2 = lsx_design_lpf(Fp, Fc, Fn, allow_aliasing, att, &num_taps, 0, -1.);
+    int k = 4 << (phase == 50 && multiplier == 4 && Fn == 4);
+    double * h2 = lsx_design_lpf(Fp, Fc, Fn, allow_aliasing, att, &num_taps, -k, -1.);
 
     if (phase != 50)
       lsx_fir_to_phase(&h2, &num_taps, &f->post_peak, phase);
-    else {
-      if (Fn == 4 && ((num_taps - 1) & 4)) { /* preserve phase */
-        double * h3 = calloc(num_taps + 4, sizeof(*h3));
-        memcpy(h3 + 2, h2, num_taps * sizeof(*h3));
-        free(h2);
-        h2 = h3;
-        num_taps += 4;
-      }
-      f->post_peak = num_taps / 2;
-    }
+    else f->post_peak = num_taps / 2;
 
     dft_length = lsx_set_dft_length(num_taps);
     f->coefs = calloc(dft_length, sizeof(*f->coefs));
@@ -313,7 +306,7 @@
       if (fracL > 2) {
         int L = fracL, M = realM;
         for (i = level + 1; i && !(L & 1); L >>= 1, --i);
-        if (L < 3 && (M <<= i) < 7) {
+        if (((M <<= i) < 7 && L < 3) || M == 4) {
           preL = L, preM = M, realM = fracL = 1, level = 0, upsample = sox_true;
           break;
         }
@@ -395,7 +388,7 @@
 
     if (preL * preM != 1) {
       init_dft_filter(shared, 0, 0, 0, bw, 1., (double)max(preL, preM), att, preL, phase, allow_aliasing);
-      setup_dft_stage(shared, 0, &pre_stage, preL, preM == 2 && !allow_aliasing? 0 : preM);
+      setup_dft_stage(shared, 0, &pre_stage, preL, preM, allow_aliasing);
       --p->input_stage_num;
     }
     else if (level && have_frac_stage && (1 - pass) / (1 - bw) > 2)
@@ -405,7 +398,7 @@
       init_dft_filter(shared, 1, 0, 0,
           bw * (upsample? factor * postL / postM : 1),
           1., (double)(upsample? postL : postM), att, postL, phase, allow_aliasing);
-      setup_dft_stage(shared, 1, &post_stage, postL, postM == 2 && !allow_aliasing? 0 : postM);
+      setup_dft_stage(shared, 1, &post_stage, postL, postM, allow_aliasing);
       ++p->output_stage_num;
     }
   }
@@ -413,12 +406,12 @@
     stage_t * s = &p->stages[i];
     if (i >= 0 && i < level - have_frac_stage) {
       s->fn = half_sample_25;
-      s->pre_post = 2 * (array_length(half_fir_coefs_25) - 1);
+      s->pre_post = 4 * array_length(half_fir_coefs_25);
       s->preload = s->pre = s->pre_post >> 1;
     }
     else if (level && i == level - 1) {
       if (shared->dft_filter[0].num_taps)
-        setup_dft_stage(shared, 0, s, 1, (int)allow_aliasing << 1);
+        setup_dft_stage(shared, 0, s, 1, 2, allow_aliasing);
       else *s = post_stage;
     }
     fifo_create(&s->fifo, (int)sizeof(sample_t));
--- a/src/rate_filters.h
+++ b/src/rate_filters.h
@@ -18,14 +18,10 @@
 /* Generated by m4 */
 
 static const sample_t half_fir_coefs_25[] = {
-  4.9866643051942178e-001, 3.1333582318860204e-001, 1.2567743716165585e-003,
- -9.2035726038137103e-002, -1.0507348255277846e-003, 4.2764945027796687e-002,
-  7.7661461450703555e-004, -2.0673365323361139e-002, -5.0429677622613805e-004,
-  9.4223774565849357e-003, 2.8491539998284476e-004, -3.8562347294894628e-003,
- -1.3803431143314762e-004, 1.3634218103234187e-003, 5.6110366313398705e-005,
- -3.9872042837864422e-004, -1.8501044952475473e-005, 9.0580351350892191e-005,
-  4.6764104835321042e-006, -1.4284332593063177e-005, -8.1340436298087893e-007,
-  1.1833367010222812e-006, 7.3979325233687461e-008,
+  3.1358440327836512e-01, -9.2701477245364594e-02, 4.3647483867630447e-02,
+  -2.1545228788689186e-02, 1.0119340890565588e-02, -4.3181204279612133e-03,
+  1.6176661095102525e-03, -5.1348399782997947e-04, 1.3185858795078468e-04,
+  -2.5493512192147390e-05, 3.2461554199264636e-06, -1.9450196215470593e-07,
 };
 static const sample_t half_fir_coefs_low[] = {
   4.2759802493108773e-001, 3.0939308096100709e-001, 6.9285325719540158e-002,
@@ -45,10 +41,27 @@
   -6.0893901283459912e-006, 1.4040363940567877e-005, 4.9834316581482789e-006,
 };
 #define FUNCTION half_sample_25 
-#define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+#define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _
 #define COEFS half_fir_coefs_25 
-assert_static(!((array_length(COEFS)- 1) & 1), HALF_FIR_LENGTH_25 );
-#include "rate_half_fir.h"
+#define _ sum += (input[-(2*j +1)] + input[(2*j +1)]) * COEFS[j], ++j;
+static void FUNCTION(stage_t * p, fifo_t * output_fifo)
+{
+  sample_t const * input = stage_read_p(p);
+  int i, num_out = (stage_occupancy(p) + 1) / 2;
+  sample_t * output = fifo_reserve(output_fifo, num_out);
+
+  for (i = 0; i < num_out; ++i, input += 2) {
+    int j = 0;
+    sample_t sum = input[0] * .5;
+    CONVOLVE
+    output[i] = sum;
+  }
+  fifo_read(&p->fifo, 2 * num_out, NULL);
+}
+#undef _
+#undef COEFS
+#undef CONVOLVE
+#undef FUNCTION
 #define FUNCTION half_sample_low
 #define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 #define COEFS half_fir_coefs_low
--- a/src/sinc.c
+++ b/src/sinc.c
@@ -80,14 +80,15 @@
 
 static double * lpf(double Fn, double Fc, double tbw, int * num_taps, double att, double * beta, sox_bool round)
 {
+  int n = *num_taps;
   if ((Fc /= Fn) <= 0 || Fc >= 1) {
     *num_taps = 0;
     return NULL;
   }
   att = att? att : 120;
-  *beta = *beta < 0? lsx_kaiser_beta(att) : *beta;
-  if (!*num_taps) {
-    int n = lsx_lpf_num_taps(att, (tbw? tbw / Fn : .05) * .5, 0);
+  lsx_kaiser_params(att, (tbw? tbw / Fn : .05) * .5, beta, num_taps);
+  if (!n) {
+    n = *num_taps;
     *num_taps = range_limit(n, 11, 32767);
     if (round)
       *num_taps = 1 + 2 * (int)((int)((*num_taps / 2) * Fc + .5) / Fc + .5);
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -101,16 +101,16 @@
 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, sox_bool dc_norm);
-int lsx_lpf_num_taps(double att, double tr_bw, int k);
+void lsx_kaiser_params(double att, double tr_bw, double * beta, int * num_taps);
 double * lsx_design_lpf(
     double Fp,      /* End of pass-band; ~= 0.01dB point */
-    double Fc,      /* Start of stop-band */
+    double Fs,      /* 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 beta);
+    int * num_taps, /* 0: value will be estimated */
+    int k,          /* >0: number of phases; <0: num_taps ≡ 1 (mod -k) */
+    double beta);   /* <0: value will be estimated */
 void lsx_fir_to_phase(double * * h, int * len,
     int * post_len, double phase0);
 #define LSX_TO_6dB .5869