1 /* Copyright (c) 20/03/2000 Fabien COELHO <fabien@coelho.net>
2  * Copyright (c) 2000-2007 SoX contributors
3  *
4  * SoX vol effect; change volume with basic linear amplitude formula.
5  * Beware of saturations!  Clipping is checked and reported.
6  *
7  * FIXME: deprecate or remove the limiter in favour of compand.
8  */
9 #define vol_usage \
10   "GAIN [TYPE [LIMITERGAIN]]\n" \
11   "\t(default TYPE=amplitude: 1 is constant, < 0 change phase;\n" \
12   "\tTYPE=power 1 is constant; TYPE=dB: 0 is constant, +6 doubles ampl.)\n" \
13   "\tThe peak limiter has a gain much less than 1 (e.g. 0.05 or 0.02) and\n" \
14   "\tis only used on peaks (to prevent clipping); default is no limiter."
15 
16 #include "sox_i.h"
17 
18 typedef struct {
19   double    gain; /* amplitude gain. */
20   sox_bool  uselimiter;
21   double    limiterthreshhold;
22   double    limitergain;
23   uint64_t  limited; /* number of limited values to report. */
24   uint64_t  totalprocessed;
25 } priv_t;
26 
27 enum {vol_amplitude, vol_dB, vol_power};
28 
29 static lsx_enum_item const vol_types[] = {
30   LSX_ENUM_ITEM(vol_,amplitude)
31   LSX_ENUM_ITEM(vol_,dB)
32   LSX_ENUM_ITEM(vol_,power)
33   {0, 0}};
34 
35 /*
36  * Process options: gain (float) type (amplitude, power, dB)
37  */
getopts(sox_effect_t * effp,int argc,char ** argv)38 static int getopts(sox_effect_t * effp, int argc, char **argv)
39 {
40   priv_t *     vol = (priv_t *) effp->priv;
41   char      type_string[11];
42   char *    type_ptr = type_string;
43   char      dummy;             /* To check for extraneous chars. */
44   sox_bool  have_type;
45   --argc, ++argv;
46 
47   vol->gain = 1;               /* Default is no change. */
48   vol->uselimiter = sox_false; /* Default is no limiter. */
49 
50   /* Get the vol, and the type if it's in the same arg. */
51   if (!argc || (have_type = sscanf(argv[0], "%lf %10s %c", &vol->gain, type_string, &dummy) - 1) > 1)
52     return lsx_usage(effp);
53   ++argv, --argc;
54 
55   /* No type yet? Get it from the next arg: */
56   if (!have_type && argc) {
57     have_type = sox_true;
58     type_ptr = *argv;
59     ++argv, --argc;
60   }
61 
62   if (have_type) {
63     lsx_enum_item const * p = lsx_find_enum_text(type_ptr, vol_types, 0);
64     if (!p)
65       return lsx_usage(effp);
66     switch (p->value) {
67       case vol_dB: vol->gain = dB_to_linear(vol->gain); break;
68       case vol_power: /* power to amplitude, keep phase change */
69         vol->gain = vol->gain > 0 ? sqrt(vol->gain) : -sqrt(-vol->gain);
70         break;
71     }
72   }
73 
74   if (argc) {
75     if (fabs(vol->gain) < 1 || sscanf(*argv, "%lf %c", &vol->limitergain, &dummy) != 1 || vol->limitergain <= 0 || vol->limitergain >= 1)
76       return lsx_usage(effp);
77 
78     vol->uselimiter = sox_true;
79     /* The following equation is derived so that there is no
80      * discontinuity in output amplitudes */
81     /* and a SOX_SAMPLE_MAX input always maps to a SOX_SAMPLE_MAX output
82      * when the limiter is activated. */
83     /* (NOTE: There **WILL** be a discontinuity in the slope
84      * of the output amplitudes when using the limiter.) */
85     vol->limiterthreshhold = SOX_SAMPLE_MAX * (1.0 - vol->limitergain) / (fabs(vol->gain) - vol->limitergain);
86   }
87   lsx_debug("mult=%g limit=%g", vol->gain, vol->limitergain);
88   return SOX_SUCCESS;
89 }
90 
91 /*
92  * Start processing
93  */
start(sox_effect_t * effp)94 static int start(sox_effect_t * effp)
95 {
96     priv_t * vol = (priv_t *) effp->priv;
97 
98     if (vol->gain == 1)
99       return SOX_EFF_NULL;
100 
101     vol->limited = 0;
102     vol->totalprocessed = 0;
103 
104     return SOX_SUCCESS;
105 }
106 
107 /*
108  * Process data.
109  */
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)110 static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
111                 size_t *isamp, size_t *osamp)
112 {
113     priv_t * vol = (priv_t *) effp->priv;
114     register double gain = vol->gain;
115     register double limiterthreshhold = vol->limiterthreshhold;
116     register double sample;
117     register size_t len;
118 
119     len = min(*osamp, *isamp);
120 
121     /* report back dealt with amount. */
122     *isamp = len; *osamp = len;
123 
124     if (vol->uselimiter)
125     {
126         vol->totalprocessed += len;
127 
128         for (;len>0; len--)
129             {
130                 sample = *ibuf++;
131 
132                 if (sample > limiterthreshhold)
133                 {
134                         sample =  (SOX_SAMPLE_MAX - vol->limitergain * (SOX_SAMPLE_MAX - sample));
135                         vol->limited++;
136                 }
137                 else if (sample < -limiterthreshhold)
138                 {
139                         sample = -(SOX_SAMPLE_MAX - vol->limitergain * (SOX_SAMPLE_MAX + sample));
140                         /* FIXME: MIN is (-MAX)-1 so need to make sure we
141                          * don't go over that.  Probably could do this
142                          * check inside the above equation but I didn't
143                          * think it thru.
144                          */
145                         if (sample < SOX_SAMPLE_MIN)
146                             sample = SOX_SAMPLE_MIN;
147                         vol->limited++;
148                 } else
149                         sample = gain * sample;
150 
151                 SOX_SAMPLE_CLIP_COUNT(sample, effp->clips);
152                *obuf++ = sample;
153             }
154     }
155     else
156     {
157         /* quite basic, with clipping */
158         for (;len>0; len--)
159         {
160                 sample = gain * *ibuf++;
161                 SOX_SAMPLE_CLIP_COUNT(sample, effp->clips);
162                 *obuf++ = sample;
163         }
164     }
165     return SOX_SUCCESS;
166 }
167 
stop(sox_effect_t * effp)168 static int stop(sox_effect_t * effp)
169 {
170   priv_t * vol = (priv_t *) effp->priv;
171   if (vol->limited) {
172     lsx_warn("limited %" PRIu64 " values (%d percent).",
173          vol->limited, (int) (vol->limited * 100.0 / vol->totalprocessed));
174   }
175   return SOX_SUCCESS;
176 }
177 
lsx_vol_effect_fn(void)178 sox_effect_handler_t const * lsx_vol_effect_fn(void)
179 {
180   static sox_effect_handler_t handler = {
181     "vol", vol_usage, SOX_EFF_MCHAN | SOX_EFF_GAIN, getopts, start, flow, 0, stop, 0, sizeof(priv_t)
182   };
183   return &handler;
184 }
185