shithub: sox

Download patch

ref: 88d38f3c68660480142e9e773864210ccbd4ad8e
parent: c18294ee0942343a69eaf701fdc9445041343b06
author: robs <robs>
date: Sun Dec 31 05:50:37 EST 2006

Use common functions for biquad filters.

--- a/src/biquad.c
+++ b/src/biquad.c
@@ -2,6 +2,51 @@
 
 
 
+int st_biquad_start(eff_t effp, char const * width_name)
+{
+  biquad_t p = (biquad_t) effp->priv;
+
+  /* Simplify: */
+  p->b2 = p->b2/p->a0;
+  p->b1 = p->b1/p->a0;
+  p->b0 = p->b0/p->a0;
+  p->a2 = p->a2/p->a0;
+  p->a1 = p->a1/p->a0;
+
+  if (p->dcNormalise) /* Normalise to AV = 0dB at DC: */
+  {
+    double normalise = (1 + p->a1 + p->a2) / (p->b0 + p->b1 + p->b2);
+    p->b0 = normalise * p->b0;
+    p->b1 = normalise * p->b1;
+    p->b2 = normalise * p->b2;
+  }
+
+  if (effp->globalinfo->octave_plot_effect)
+  {
+    printf(
+      "title('SoX effect: %s gain=%g centre=%g %s=%g (rate=%u)')\n"
+      "xlabel('Frequency (Hz)')\n"
+      "ylabel('Amplitude Response (dB)')\n"
+      "Fs=%u;minF=10;maxF=Fs/2;\n"
+      "axis([minF maxF -25 25])\n"
+      "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+      "grid on\n"
+      "[h,w]=freqz([%f %f %f],[1 %f %f],sweepF,Fs);\n"
+      "semilogx(w,20*log10(h),'b')\n"
+      "pause\n"
+      , effp->name, p->gain, p->fc, width_name, p->width.q
+      , effp->ininfo.rate, effp->ininfo.rate
+      , p->b0, p->b1, p->b2, p->a1, p->a2
+      );
+    exit(0);
+  }
+
+  p->o2 = p->o1 = p->i2 = p-> i1 = 0;
+  return ST_SUCCESS;
+}
+
+
+  
 int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf, 
                         st_size_t *isamp, st_size_t *osamp)
 {
--- a/src/biquad.h
+++ b/src/biquad.h
@@ -9,16 +9,17 @@
 {
   double gain;
   double fc;               /* Centre/corner/cutoff frequency */
-  double oomph;            /* Q, BW, or Slope */
+  union {double q, bandwidth, slope;} width; /* Depending on filter type */
   bool dcNormalise;        /* A treble filter should normalise at DC */
 
   double b2, b1, b0;       /* Filter coefficients */
-  double a2, a1;           /* Filter coefficients; a0 = 1 */
+  double a2, a1, a0;       /* Filter coefficients */
 
   st_sample_t i1, i2;      /* Filter memory */
   double o1, o2;           /* Filter memory */
 } * biquad_t;
 
+int st_biquad_start(eff_t effp, char const * width_name);
 int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf, 
                         st_size_t *isamp, st_size_t *osamp);
 
--- a/src/equalizer.c
+++ b/src/equalizer.c
@@ -48,48 +48,28 @@
                 1 + (a1/a0)z^-1 + (a2/a0)z^-2
  */
 
-#include <math.h>
-#include "st_i.h"
+#include "biquad.h"
 
-static st_effect_t st_equalizer_effect;
-
-/* Filter parameters */
-typedef struct filterparams {
-  float rate;  /* Sample rate */
-  float Q;     /* Q-factor */
-  float cfreq; /* Central frequency (Hz) */
-  float gain;  /* Gain (dB) */
-  double x[3]; /* In where x[2] <=> x[ n - 2 ] */
-  double y[3]; /* Out */
-  double b[3]; /* From this point, equation constants... */
-  double a[3];
-} *equalizer_t;
-
-static int st_equalizer_getopts(eff_t effp, int n, char **argv) 
+static int equalizer_getopts(eff_t effp, int n, char **argv) 
 {
-  equalizer_t eq = (equalizer_t) effp->priv;
-  int i;
+  biquad_t eq = (biquad_t) effp->priv;
+  char dummy;
 
-  if (n < 3)
-    {
-      st_fail("Usage: equalizer center-freq Q gain");
-      return (ST_EOF);
-    }
-
-  i = 0;
-  sscanf(argv[i++], "%f", &eq->cfreq);
-  sscanf(argv[i++], "%f", &eq->Q);
-  sscanf(argv[i++], "%f", &eq->gain);
-
-  /* FIXME: Would be nice to validate the params.. */
-
-  return (ST_SUCCESS);
+  if (n != 3 ||
+      sscanf(argv[0], "%lf %c", &eq->fc   , &dummy) != 1 ||
+      sscanf(argv[1], "%lf %c", &eq->width.q, &dummy) != 1 ||
+      sscanf(argv[2], "%lf %c", &eq->gain , &dummy) != 1 ||
+      eq->fc <= 0 ||
+      eq->width.q <= 0) {
+    st_fail(effp->h->usage);
+    return ST_EOF;
+  }
+  return ST_SUCCESS;
 }
 
-/* Set the filter constants */
-static int st_equalizer_start(eff_t effp)
+static int equalizer_start(eff_t effp)
 {
-  equalizer_t eq = (equalizer_t) effp->priv;
+  biquad_t eq = (biquad_t) effp->priv;
   double w0;
   double amp;
   double alpha;
@@ -97,108 +77,41 @@
   if (eq->gain == 0)
     return ST_EFF_NULL;
 
-  /* Sample rate */
-  eq->rate = effp->ininfo.rate;
-
-  w0 = 2*M_PI*eq->cfreq/eq->rate;
+  /* Set the filter constants */
+  w0 = 2*M_PI*eq->fc/effp->ininfo.rate;
   amp = pow( 10, eq->gain/40 );
-  alpha = sin(w0)/( 2*eq->Q );
+  alpha = sin(w0)/( 2*eq->width.q );
 
-  st_debug("cfreq: %fHz", eq->cfreq);
-  st_debug("Q: %f", eq->Q);
+  st_debug("cfreq: %fHz", eq->fc);
+  st_debug("Q: %f", eq->width.q);
   st_debug("gain: %fdB", eq->gain);
-  st_debug("rate: %f", eq->rate);
   st_debug("w0: %f", w0);
   st_debug("amp: %f", amp);
   st_debug("alpha: %f", alpha);
 
   /* Initialisation */
-  eq->b[0] =  1 + alpha*amp;
-  eq->b[1] = -2*cos(w0);
-  eq->b[2] =  1 - alpha*amp;
-  eq->a[0] =  1 + alpha/amp;
-  eq->a[1] = -2*cos(w0);
-  eq->a[2] =  1 - alpha/amp;
+  eq->b0 =  1 + alpha*amp;
+  eq->b1 = -2*cos(w0);
+  eq->b2 =  1 - alpha*amp;
+  eq->a0 =  1 + alpha/amp;
+  eq->a1 = -2*cos(w0);
+  eq->a2 =  1 - alpha/amp;
 
-  eq->x[0] = 0; /* x[n] */
-  eq->x[1] = 0; /* x[n-1] */
-  eq->x[2] = 0; /* x[n-2] */
-  eq->y[0] = 0; /* y[n] */
-  eq->y[1] = 0; /* y[n-1] */
-  eq->y[2] = 0; /* y[n-2] */
-
-  if (effp->globalinfo->octave_plot_effect)
-  {
-    printf(
-      "title('SoX effect: %s gain=%g centre=%g Q=%g (rate=%u)')\n"
-      "xlabel('Frequency (Hz)')\n"
-      "ylabel('Amplitude Response (dB)')\n"
-      "Fs=%u;minF=10;maxF=Fs/2;\n"
-      "axis([minF maxF -25 25])\n"
-      "sweepF=logspace(log10(minF),log10(maxF),200);\n"
-      "grid on\n"
-      "[h,w]=freqz([%f %f %f],[%f %f %f],sweepF,Fs);\n"
-      "semilogx(w,20*log10(h),'b')\n"
-      "pause\n"
-      , effp->name, eq->gain, eq->cfreq, eq->Q
-      , effp->ininfo.rate, effp->ininfo.rate
-      , eq->b[0], eq->b[1], eq->b[2], eq->a[0], eq->a[1], eq->a[2]
-      );
-    exit(0);
-  }
-
-  return (ST_SUCCESS);
+  return st_biquad_start(effp, "Q");
 }
 
-static int st_equalizer_flow(eff_t effp, const st_sample_t *ibuf,
-                      st_sample_t *obuf, st_size_t *isamp,
-                      st_size_t *osamp)
-{
-  equalizer_t eq = (equalizer_t) effp->priv;
-  st_size_t len, done;
-  double out;
-
-  len = ((*isamp > *osamp) ? *osamp : *isamp);
-
-  for(done = 0; done < len; done++) {
-    eq->x[2] = eq->x[1];
-    eq->x[1] = eq->x[0];
-    eq->x[0] = *ibuf++;
-      
-    eq->y[2] = eq->y[1];
-    eq->y[1] = eq->y[0];
-    out = (
-           (eq->b[0]/eq->a[0])*eq->x[0] +
-           (eq->b[1]/eq->a[0])*eq->x[1] +
-           (eq->b[2]/eq->a[0])*eq->x[2] -
-           (eq->a[1]/eq->a[0])*eq->y[1] -
-           (eq->a[2]/eq->a[0])*eq->y[2]
-           );
-    eq->y[0] = out;
-
-    ST_SAMPLE_CLIP_COUNT(out, effp->clippedCount);
-    *obuf++ = out;
-  }
-
-  *isamp = len;
-  *osamp = len;
-
-  return (ST_SUCCESS);
-}
-
-static st_effect_t st_equalizer_effect = {
-  "equalizer",
-  "Usage: equalizer central-frequency Q gain",
-  0,
-  st_equalizer_getopts,
-  st_equalizer_start,
-  st_equalizer_flow,
-  st_effect_nothing_drain,
-  st_effect_nothing,
-  st_effect_nothing
-};
-
 const st_effect_t *st_equalizer_effect_fn(void)
 {
-    return &st_equalizer_effect;
+  static st_effect_t driver = {
+    "equalizer",
+    "Usage: equalizer central-frequency Q gain",
+    0,
+    equalizer_getopts,
+    equalizer_start,
+    st_biquad_flow,
+    st_effect_nothing_drain,
+    st_effect_nothing,
+    st_effect_nothing
+  };
+  return &driver;
 }
--- a/src/tone.c
+++ b/src/tone.c
@@ -13,8 +13,8 @@
 
 #include "biquad.h"
 
-#include <string.h>
 
+
 static int getopts(eff_t effp, int n, char **argv, double fc, int dcNormalise)
 {
   bool isFcSet = false;
@@ -21,26 +21,24 @@
   double opt1 = HUGE_VAL, opt2 = HUGE_VAL;
   biquad_t p = (biquad_t) effp->priv;
   int ret = ST_SUCCESS;
+  char dummy;
   
-  /* Zero all numbers, set all bools to false: */
-  memset(p, 0, sizeof(*p));
-
   /* Initialise non-zero numbers: */
   p->dcNormalise = dcNormalise;
   p->fc = fc;
-  p->oomph = 0.5;
+  p->width.slope = 0.5;
 
   /*
    * This block is a little complicated -- all because I wanted
    * to make it easy for the user i.e. order doesn't matter for
    * optional arguments.
-   * This is made possible because slope (or oomph) <= 1 and by
+   * This is made possible because slope <= 1 and by
    * insisting that the centre-frequency is > 1 (Hz).
    */
   if (n > 0 &&
-     sscanf(argv[0], "%lf", &p->gain) &&
-     (n < 2 || sscanf(argv[1], "%lf", &opt1))  &&
-     (n < 3 || sscanf(argv[2], "%lf", &opt2))  &&
+     sscanf(argv[0], "%lf %c", &p->gain, &dummy) == 1 &&
+     (n < 2 || sscanf(argv[1], "%lf %c", &opt1, &dummy) == 1)  &&
+     (n < 3 || sscanf(argv[2], "%lf %c", &opt2, &dummy) == 1)  &&
      (n < 4)) {
     if (opt1 != HUGE_VAL) {
       if (opt1 <= 0)
@@ -50,7 +48,7 @@
           p->fc = opt1;
           isFcSet = true;
         } else
-          p->oomph = opt1;
+          p->width.slope = opt1;
         if (opt2 != HUGE_VAL) {
           if (opt2 > 1) {
             if (isFcSet)
@@ -61,7 +59,7 @@
             if (!isFcSet)
               ret = ST_EOF;
             else
-              p->oomph = opt2;
+              p->width.slope = opt2;
           } else
             ret = ST_EOF;
         }
@@ -73,14 +71,13 @@
 
   if (ret == ST_EOF)
     st_fail(effp->h->usage);
-
   return ret;
 }
 
 
 
-static int st_bass_getopts  (eff_t e, int n, char **a) {return getopts(e, n, a,  100, 0);}
-static int st_treble_getopts(eff_t e, int n, char **a) {return getopts(e, n, a, 3000, 1);}
+static int bass_getopts  (eff_t e,int n,char **a){return getopts(e,n,a, 100,0);}
+static int treble_getopts(eff_t e,int n,char **a){return getopts(e,n,a,3000,1);}
 
 
 
@@ -91,92 +88,51 @@
   /* Calculate intermediates: */
   double A  = exp(p->gain / 40 * log(10));
   double w0 = 2 * M_PI * p->fc / effp->ininfo.rate;
-  double alpha = sin(w0)/2 * sqrt( (A + 1/A)*(1/p->oomph - 1) + 2 );
-  double a0;
+  double alpha = sin(w0)/2 * sqrt( (A + 1/A)*(1/p->width.slope - 1) + 2 );
 
   /* Calculate filter coefficients: */
-  p->b0 =    A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha );  /* Numerator. */
+  p->b0 =    A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha );
   p->b1 =  2*A*( (A-1) - (A+1)*cos(w0)                   );
   p->b2 =    A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha );
-     a0 =        (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha;    /* Denominator. */
+  p->a0 =        (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha;
   p->a1 =   -2*( (A-1) + (A+1)*cos(w0)                   );
   p->a2 =        (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha;
 
-  /* Simplify: */
-  p->b2 = p->b2/a0;
-  p->b1 = p->b1/a0;
-  p->b0 = p->b0/a0;
-  p->a2 = p->a2/a0;
-  p->a1 = p->a1/a0;
-
-  if (p->dcNormalise) /* Normalise to AV = 0dB at DC: */
-  {
-    double normalise = (1 + p->a1 + p->a2) / (p->b0 + p->b1 + p->b2);
-    p->b0 = normalise * p->b0;
-    p->b1 = normalise * p->b1;
-    p->b2 = normalise * p->b2;
-  }
-
-  if (effp->globalinfo->octave_plot_effect)
-  {
-    printf(
-      "title('SoX effect: %s gain=%g centre=%g slope=%g (rate=%u)')\n"
-      "xlabel('Frequency (Hz)')\n"
-      "ylabel('Amplitude Response (dB)')\n"
-      "Fs=%u;minF=10;maxF=Fs/2;\n"
-      "axis([minF maxF -25 25])\n"
-      "sweepF=logspace(log10(minF),log10(maxF),200);\n"
-      "grid on\n"
-      "[h,w]=freqz([%f %f %f],[1 %f %f],sweepF,Fs);\n"
-      "semilogx(w,20*log10(h),'b')\n"
-      "pause\n"
-      , effp->name, p->gain, p->fc, p->oomph
-      , effp->ininfo.rate, effp->ininfo.rate
-      , p->b0, p->b1, p->b2, p->a1, p->a2
-      );
-    exit(0);
-  }
-  return ST_SUCCESS;
+  return st_biquad_start(effp, "slope");
 }
 
 
 
-static st_effect_t st_bass_effect = {
-  "bass",
-  "Usage: bass gain [frequency] [slope]",
-  0,
-  st_bass_getopts,
-  st_biquad_shelf_start,
-  st_biquad_flow,
-  st_effect_nothing_drain,
-  st_effect_nothing,
-  st_effect_nothing
-};
-
-
-
 st_effect_t const * st_bass_effect_fn(void)
 {
-  return &st_bass_effect;
+  static st_effect_t driver = {
+    "bass",
+    "Usage: bass gain [frequency] [slope]",
+    0,
+    bass_getopts,
+    st_biquad_shelf_start,
+    st_biquad_flow,
+    st_effect_nothing_drain,
+    st_effect_nothing,
+    st_effect_nothing
+  };
+  return &driver;
 }
 
 
 
-static st_effect_t st_treble_effect = {
-  "treble",
-  "Usage: treble gain [frequency] [slope]",
-  0,
-  st_treble_getopts,
-  st_biquad_shelf_start,
-  st_biquad_flow,
-  st_effect_nothing_drain,
-  st_effect_nothing,
-  st_effect_nothing
-};
-
-
-
 st_effect_t const * st_treble_effect_fn(void)
 {
-  return &st_treble_effect;
+  static st_effect_t driver = {
+    "treble",
+    "Usage: treble gain [frequency] [slope]",
+    0,
+    treble_getopts,
+    st_biquad_shelf_start,
+    st_biquad_flow,
+    st_effect_nothing_drain,
+    st_effect_nothing,
+    st_effect_nothing
+  };
+  return &driver;
 }