1 /* Effect: loudness filter     Copyright (c) 2008 robs@users.sourceforge.net
2  *
3  * This library is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2.1 of the License, or (at
6  * your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library; if not, write to the Free Software Foundation,
15  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
16  */
17 
18 #include "sox_i.h"
19 #include "dft_filter.h"
20 #include <string.h>
21 
22 typedef struct {
23   dft_filter_priv_t base;
24   double     delta, start;
25   int        n;
26 } priv_t;
27 
create(sox_effect_t * effp,int argc,char ** argv)28 static int create(sox_effect_t * effp, int argc, char **argv)
29 {
30   priv_t * p = (priv_t *)effp->priv;
31   dft_filter_priv_t * b = &p->base;
32   b->filter_ptr = &b->filter;
33   p->delta = -10;
34   p->start = 65;
35   p->n = 1023;
36   --argc, ++argv;
37   do {                    /* break-able block */
38     NUMERIC_PARAMETER(delta,-50 , 15) /* FIXME expand range */
39     NUMERIC_PARAMETER(start, 50 , 75) /* FIXME expand range */
40     NUMERIC_PARAMETER(n    ,127 ,2047)
41   } while (0);
42   p->n = 2 * p->n + 1;
43   return argc? lsx_usage(effp) : SOX_SUCCESS;
44 }
45 
make_filter(int n,double start,double delta,double rate)46 static double * make_filter(int n, double start, double delta, double rate)
47 {
48   static const struct {double f, af, lu, tf;} iso226_table[] = {
49     {   20,0.532,-31.6,78.5},{   25,0.506,-27.2,68.7},{ 31.5,0.480,-23.0,59.5},
50     {   40,0.455,-19.1,51.1},{   50,0.432,-15.9,44.0},{   63,0.409,-13.0,37.5},
51     {   80,0.387,-10.3,31.5},{  100,0.367, -8.1,26.5},{  125,0.349, -6.2,22.1},
52     {  160,0.330, -4.5,17.9},{  200,0.315, -3.1,14.4},{  250,0.301, -2.0,11.4},
53     {  315,0.288, -1.1, 8.6},{  400,0.276, -0.4, 6.2},{  500,0.267,  0.0, 4.4},
54     {  630,0.259,  0.3, 3.0},{  800,0.253,  0.5, 2.2},{ 1000,0.250,  0.0, 2.4},
55     { 1250,0.246, -2.7, 3.5},{ 1600,0.244, -4.1, 1.7},{ 2000,0.243, -1.0,-1.3},
56     { 2500,0.243,  1.7,-4.2},{ 3150,0.243,  2.5,-6.0},{ 4000,0.242,  1.2,-5.4},
57     { 5000,0.242, -2.1,-1.5},{ 6300,0.245, -7.1, 6.0},{ 8000,0.254,-11.2,12.6},
58     {10000,0.271,-10.7,13.9},{12500,0.301, -3.1,12.3},
59   };
60   #define LEN (array_length(iso226_table) + 2)
61   #define SPL(phon, t) (10 / t.af * log10(4.47e-3 * (pow(10., .025 * (phon)) - \
62           1.15) + pow(.4 * pow(10., (t.tf + t.lu) / 10 - 9), t.af)) - t.lu + 94)
63   double fs[LEN], spl[LEN], d[LEN], * work, * h;
64   int i, work_len;
65 
66   fs[0] = log(1.);
67   spl[0] = delta * .2;
68   for (i = 0; i < (int)LEN - 2; ++i) {
69     spl[i + 1] = SPL(start + delta, iso226_table[i]) -
70                  SPL(start        , iso226_table[i]);
71     fs[i + 1] = log(iso226_table[i].f);
72   }
73   fs[i + 1] = log(100000.);
74   spl[i + 1] = spl[0];
75   lsx_prepare_spline3(fs, spl, (int)LEN, HUGE_VAL, HUGE_VAL, d);
76 
77   for (work_len = 8192; work_len < rate / 2; work_len <<= 1);
78   work = lsx_calloc(work_len, sizeof(*work));
79   h = lsx_calloc(n, sizeof(*h));
80 
81   for (i = 0; i <= work_len / 2; ++i) {
82     double f = rate * i / work_len;
83     double spl1 = f < 1? spl[0] : lsx_spline3(fs, spl, d, (int)LEN, log(f));
84     work[i < work_len / 2 ? 2 * i : 1] = dB_to_linear(spl1);
85   }
86   lsx_safe_rdft(work_len, -1, work);
87   for (i = 0; i < n; ++i)
88     h[i] = work[(work_len - n / 2 + i) % work_len] * 2. / work_len;
89   lsx_apply_kaiser(h, n, lsx_kaiser_beta(40 + 2./3 * fabs(delta), .1));
90 
91   free(work);
92   return h;
93   #undef SPL
94   #undef LEN
95 }
96 
start(sox_effect_t * effp)97 static int start(sox_effect_t * effp)
98 {
99   priv_t * p = (priv_t *) effp->priv;
100   dft_filter_t * f = p->base.filter_ptr;
101 
102   if (p->delta == 0)
103     return SOX_EFF_NULL;
104 
105   if (!f->num_taps) {
106     double * h = make_filter(p->n, p->start, p->delta, effp->in_signal.rate);
107     if (effp->global_info->plot != sox_plot_off) {
108       char title[100];
109       sprintf(title, "SoX effect: loudness %g (%g)", p->delta, p->start);
110       lsx_plot_fir(h, p->n, effp->in_signal.rate,
111           effp->global_info->plot, title, p->delta - 5, 0.);
112       return SOX_EOF;
113     }
114     lsx_set_dft_filter(f, h, p->n, p->n >> 1);
115   }
116   return lsx_dft_filter_effect_fn()->start(effp);
117 }
118 
lsx_loudness_effect_fn(void)119 sox_effect_handler_t const * lsx_loudness_effect_fn(void)
120 {
121   static sox_effect_handler_t handler;
122   handler = *lsx_dft_filter_effect_fn();
123   handler.name = "loudness";
124   handler.usage = "[gain [ref]]";
125   handler.getopts = create;
126   handler.start = start;
127   handler.priv_size = sizeof(priv_t);
128   return &handler;
129 }
130