1 /* libSoX effect: Hilbert transform filter
2  *
3  * First version of this effect written 11/2011 by Ulrich Klauer, using maths
4  * from "Understanding digital signal processing" by Richard G. Lyons.
5  *
6  * Copyright 2011 Chris Bagwell and SoX Contributors
7  *
8  * This library is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or (at
11  * your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "sox_i.h"
24 #include "dft_filter.h"
25 
26 typedef struct {
27   dft_filter_priv_t base;
28   double *h;
29   int taps;
30 } priv_t;
31 
getopts(sox_effect_t * effp,int argc,char ** argv)32 static int getopts(sox_effect_t *effp, int argc, char **argv)
33 {
34   lsx_getopt_t optstate;
35   int c;
36   priv_t *p = (priv_t*)effp->priv;
37   dft_filter_priv_t *b = &p->base;
38 
39   b->filter_ptr = &b->filter;
40 
41   lsx_getopt_init(argc, argv, "+n:", NULL, lsx_getopt_flag_none, 1, &optstate);
42 
43   while ((c = lsx_getopt(&optstate)) != -1) switch (c) {
44     GETOPT_NUMERIC(optstate, 'n', taps, 3, 32767)
45     default: lsx_fail("invalid option `-%c'", optstate.opt); return lsx_usage(effp);
46   }
47   if (p->taps && p->taps%2 == 0) {
48     lsx_fail("only filters with an odd number of taps are supported");
49     return SOX_EOF;
50   }
51   return optstate.ind != argc ? lsx_usage(effp) : SOX_SUCCESS;
52 }
53 
start(sox_effect_t * effp)54 static int start(sox_effect_t *effp)
55 {
56   priv_t *p = (priv_t*)effp->priv;
57   dft_filter_t *f = p->base.filter_ptr;
58 
59   if (!f->num_taps) {
60     int i;
61     if (!p->taps) {
62       p->taps = effp->in_signal.rate/76.5 + 2;
63       p->taps += 1 - (p->taps%2);
64       /* results in a cutoff frequency of about 75 Hz with a Blackman window */
65       lsx_debug("choosing number of taps = %d (override with -n)", p->taps);
66     }
67     lsx_valloc(p->h, p->taps);
68     for (i = 0; i < p->taps; i++) {
69       int k = -(p->taps/2) + i;
70       if (k%2 == 0) {
71         p->h[i] = 0.0;
72       } else {
73         double pk = M_PI * k;
74         p->h[i] = (1 - cos(pk))/pk;
75       }
76     }
77     lsx_apply_blackman(p->h, p->taps, .16);
78 
79     if (effp->global_info->plot != sox_plot_off) {
80       char title[100];
81       sprintf(title, "SoX effect: hilbert (%d taps)", p->taps);
82       lsx_plot_fir(p->h, p->taps, effp->in_signal.rate,
83           effp->global_info->plot, title, -20., 5.);
84       free(p->h);
85       return SOX_EOF;
86     }
87     lsx_set_dft_filter(f, p->h, p->taps, p->taps/2);
88   }
89   return lsx_dft_filter_effect_fn()->start(effp);
90 }
91 
lsx_hilbert_effect_fn(void)92 sox_effect_handler_t const *lsx_hilbert_effect_fn(void)
93 {
94   static sox_effect_handler_t handler;
95   handler = *lsx_dft_filter_effect_fn();
96   handler.name = "hilbert";
97   handler.usage = "[-n taps]";
98   handler.getopts = getopts;
99   handler.start = start;
100   handler.priv_size = sizeof(priv_t);
101   return &handler;
102 }
103