shithub: sox

ref: 021c7886e4ecfe58022848aa3f4877e1986e3089
dir: /src/hilbert.c/

View raw version
/* libSoX effect: Hilbert transform filter
 *
 * First version of this effect written 11/2011 by Ulrich Klauer, using maths
 * from "Understanding digital signal processing" by Richard G. Lyons.
 *
 * Copyright 2011 Chris Bagwell and SoX 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 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
 */

#include "sox_i.h"
#include "dft_filter.h"

typedef struct {
  dft_filter_priv_t base;
  double *h;
  int taps;
} priv_t;

static int getopts(sox_effect_t *effp, int argc, char **argv)
{
  lsx_getopt_t optstate;
  int c;
  priv_t *p = (priv_t*)effp->priv;
  dft_filter_priv_t *b = &p->base;

  b->filter_ptr = &b->filter;

  lsx_getopt_init(argc, argv, "+n:", NULL, lsx_getopt_flag_none, 1, &optstate);

  while ((c = lsx_getopt(&optstate)) != -1) switch (c) {
    GETOPT_NUMERIC(optstate, 'n', taps, 3, 32767)
    default: lsx_fail("invalid option `-%c'", optstate.opt); return lsx_usage(effp);
  }
  if (p->taps && p->taps%2 == 0) {
    lsx_fail("only filters with an odd number of taps are supported");
    return SOX_EOF;
  }
  return optstate.ind != argc ? lsx_usage(effp) : SOX_SUCCESS;
}

static int start(sox_effect_t *effp)
{
  priv_t *p = (priv_t*)effp->priv;
  dft_filter_t *f = p->base.filter_ptr;

  if (!f->num_taps) {
    int i;
    if (!p->taps) {
      p->taps = effp->in_signal.rate/76.5 + 2;
      p->taps += 1 - (p->taps%2);
      /* results in a cutoff frequency of about 75 Hz with a Blackman window */
      lsx_debug("choosing number of taps = %d (override with -n)", p->taps);
    }
    lsx_valloc(p->h, p->taps);
    for (i = 0; i < p->taps; i++) {
      int k = -(p->taps/2) + i;
      if (k%2 == 0) {
        p->h[i] = 0.0;
      } else {
        double pk = M_PI * k;
        p->h[i] = (1 - cos(pk))/pk;
      }
    }
    lsx_apply_blackman(p->h, p->taps, .16);

    if (effp->global_info->plot != sox_plot_off) {
      char title[100];
      sprintf(title, "SoX effect: hilbert (%d taps)", p->taps);
      lsx_plot_fir(p->h, p->taps, effp->in_signal.rate,
          effp->global_info->plot, title, -20., 5.);
      free(p->h);
      return SOX_EOF;
    }
    lsx_set_dft_filter(f, p->h, p->taps, p->taps/2);
  }
  return lsx_dft_filter_effect_fn()->start(effp);
}

sox_effect_handler_t const *lsx_hilbert_effect_fn(void)
{
  static sox_effect_handler_t handler;
  handler = *lsx_dft_filter_effect_fn();
  handler.name = "hilbert";
  handler.usage = "[-n taps]";
  handler.getopts = getopts;
  handler.start = start;
  handler.priv_size = sizeof(priv_t);
  return &handler;
}