1 /* libSoX dcshift.c
2  * (c) 2000.04.15 Chris Ausbrooks <weed@bucket.pp.ualr.edu>
3  *
4  * based on vol.c which is
5  * (c) 20/03/2000 Fabien COELHO <fabien@coelho.net> for sox.
6  *
7  * DC shift a sound file, with basic linear amplitude formula.
8  * Beware of saturations! clipping is checked and reported.
9  * Cannot handle different number of channels.
10  * Cannot handle rate change.
11  */
12 
13 #include "sox_i.h"
14 
15 typedef struct {
16     double dcshift; /* DC shift. */
17     int uselimiter; /* boolean: are we using the limiter? */
18     double limiterthreshhold;
19     double limitergain; /* limiter gain. */
20     uint64_t limited; /* number of limited values to report. */
21     uint64_t totalprocessed;
22 } priv_t;
23 
24 /*
25  * Process options: dcshift (double) type (amplitude, power, dB)
26  */
sox_dcshift_getopts(sox_effect_t * effp,int argc,char ** argv)27 static int sox_dcshift_getopts(sox_effect_t * effp, int argc, char **argv)
28 {
29     priv_t * dcs = (priv_t *) effp->priv;
30     dcs->dcshift = 1.0; /* default is no change */
31     dcs->uselimiter = 0; /* default is no limiter */
32 
33   --argc, ++argv;
34     if (argc < 1)
35       return lsx_usage(effp);
36 
37     if (argc && (!sscanf(argv[0], "%lf", &dcs->dcshift)))
38       return lsx_usage(effp);
39 
40     if (argc>1)
41     {
42         if (!sscanf(argv[1], "%lf", &dcs->limitergain))
43           return lsx_usage(effp);
44 
45         dcs->uselimiter = 1; /* ok, we'll use it */
46         /* The following equation is derived so that there is no
47          * discontinuity in output amplitudes */
48         /* and a SOX_SAMPLE_MAX input always maps to a SOX_SAMPLE_MAX output
49          * when the limiter is activated. */
50         /* (NOTE: There **WILL** be a discontinuity in the slope of the
51          * output amplitudes when using the limiter.) */
52         dcs->limiterthreshhold = SOX_SAMPLE_MAX * (1.0 - (fabs(dcs->dcshift) - dcs->limitergain));
53     }
54 
55     return SOX_SUCCESS;
56 }
57 
58 /*
59  * Start processing
60  */
sox_dcshift_start(sox_effect_t * effp)61 static int sox_dcshift_start(sox_effect_t * effp)
62 {
63     priv_t * dcs = (priv_t *) effp->priv;
64 
65     if (dcs->dcshift == 0)
66       return SOX_EFF_NULL;
67 
68     dcs->limited = 0;
69     dcs->totalprocessed = 0;
70 
71     return SOX_SUCCESS;
72 }
73 
74 /*
75  * Process data.
76  */
sox_dcshift_flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)77 static int sox_dcshift_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
78                     size_t *isamp, size_t *osamp)
79 {
80     priv_t * dcs = (priv_t *) effp->priv;
81     double dcshift = dcs->dcshift;
82     double limitergain = dcs->limitergain;
83     double limiterthreshhold = dcs->limiterthreshhold;
84     double sample;
85     size_t len;
86 
87     len = min(*osamp, *isamp);
88 
89     /* report back dealt with amount. */
90     *isamp = len; *osamp = len;
91 
92     if (dcs->uselimiter)
93     {
94         dcs->totalprocessed += len;
95 
96         for (;len>0; len--)
97             {
98                 sample = *ibuf++;
99 
100                 if (sample > limiterthreshhold && dcshift > 0)
101                 {
102                         sample =  (sample - limiterthreshhold) * limitergain / (SOX_SAMPLE_MAX - limiterthreshhold) + limiterthreshhold + dcshift;
103                         dcs->limited++;
104                 }
105                 else if (sample < -limiterthreshhold && dcshift < 0)
106                 {
107                         /* Note this should really be SOX_SAMPLE_MIN but
108                          * the clip() below will take care of the overflow.
109                          */
110                         sample =  (sample + limiterthreshhold) * limitergain / (SOX_SAMPLE_MAX - limiterthreshhold) - limiterthreshhold + dcshift;
111                         dcs->limited++;
112                 }
113                 else
114                 {
115                         /* Note this should consider SOX_SAMPLE_MIN but
116                          * the clip() below will take care of the overflow.
117                          */
118                         sample = dcshift * SOX_SAMPLE_MAX + sample;
119                 }
120 
121                 SOX_SAMPLE_CLIP_COUNT(sample, effp->clips);
122                 *obuf++ = sample;
123             }
124     }
125     else for (; len > 0; --len) {            /* quite basic, with clipping */
126       double d = dcshift * (SOX_SAMPLE_MAX + 1.) + *ibuf++;
127       *obuf++ = SOX_ROUND_CLIP_COUNT(d, effp->clips);
128     }
129     return SOX_SUCCESS;
130 }
131 
132 /*
133  * Do anything required when you stop reading samples.
134  * Don't close input file!
135  */
sox_dcshift_stop(sox_effect_t * effp)136 static int sox_dcshift_stop(sox_effect_t * effp)
137 {
138     priv_t * dcs = (priv_t *) effp->priv;
139 
140     if (dcs->limited)
141     {
142         lsx_warn("DCSHIFT limited %" PRIu64 " values (%d percent).",
143              dcs->limited, (int) (dcs->limited * 100.0 / dcs->totalprocessed));
144     }
145     return SOX_SUCCESS;
146 }
147 
148 static sox_effect_handler_t sox_dcshift_effect = {
149    "dcshift",
150    "shift [ limitergain ]\n"
151    "\tThe peak limiter has a gain much less than 1.0 (ie 0.05 or 0.02) which\n"
152    "\tis only used on peaks to prevent clipping. (default is no limiter)",
153    SOX_EFF_MCHAN | SOX_EFF_GAIN,
154    sox_dcshift_getopts,
155    sox_dcshift_start,
156    sox_dcshift_flow,
157    NULL,
158    sox_dcshift_stop,
159   NULL, sizeof(priv_t)
160 };
161 
lsx_dcshift_effect_fn(void)162 const sox_effect_handler_t *lsx_dcshift_effect_fn(void)
163 {
164     return &sox_dcshift_effect;
165 }
166