1 /* libSoX compander effect
2  *
3  * Written by Nick Bailey (nick@bailey-family.org.uk or
4  *                         n.bailey@elec.gla.ac.uk)
5  *
6  * Copyright 1999 Chris Bagwell And Nick Bailey
7  * This source code is freely redistributable and may be used for
8  * any purpose.  This copyright notice must be maintained.
9  * Chris Bagwell And Nick Bailey are not responsible for
10  * the consequences of using this software.
11  */
12 
13 #include "sox_i.h"
14 
15 #include <string.h>
16 #include <stdlib.h>
17 #include "compandt.h"
18 
19 /*
20  * Compressor/expander effect for libSoX.
21  *
22  * Flow diagram for one channel:
23  *
24  *               ------------      ---------------
25  *              |            |    |               |     ---
26  * ibuff ---+---| integrator |--->| transfer func |--->|   |
27  *          |   |            |    |               |    |   |
28  *          |    ------------      ---------------     |   |  * gain
29  *          |                                          | * |----------->obuff
30  *          |       -------                            |   |
31  *          |      |       |                           |   |
32  *          +----->| delay |-------------------------->|   |
33  *                 |       |                            ---
34  *                  -------
35  */
36 #define compand_usage \
37   "attack1,decay1{,attack2,decay2} [soft-knee-dB:]in-dB1[,out-dB1]{,in-dB2,out-dB2} [gain [initial-volume-dB [delay]]]\n" \
38   "\twhere {} means optional and repeatable and [] means optional.\n" \
39   "\tdB values are floating point or -inf'; times are in seconds."
40 /*
41  * Note: clipping can occur if the transfer function pushes things too
42  * close to 0 dB.  In that case, use a negative gain, or reduce the
43  * output level of the transfer function.
44  */
45 
46 typedef struct {
47   sox_compandt_t transfer_fn;
48 
49   struct {
50     double attack_times[2]; /* 0:attack_time, 1:decay_time */
51     double volume;          /* Current "volume" of each channel */
52   } * channels;
53   unsigned expectedChannels;/* Also flags that channels aren't to be treated
54                                individually when = 1 and input not mono */
55   double delay;             /* Delay to apply before companding */
56   sox_sample_t *delay_buf;   /* Old samples, used for delay processing */
57   ptrdiff_t delay_buf_size;/* Size of delay_buf in samples */
58   ptrdiff_t delay_buf_index; /* Index into delay_buf */
59   ptrdiff_t delay_buf_cnt; /* No. of active entries in delay_buf */
60   int delay_buf_full;       /* Shows buffer situation (important for drain) */
61 
62   char *arg0;  /* copies of arguments, so that they may be modified */
63   char *arg1;
64   char *arg2;
65 } priv_t;
66 
getopts(sox_effect_t * effp,int argc,char ** argv)67 static int getopts(sox_effect_t * effp, int argc, char * * argv)
68 {
69   priv_t * l = (priv_t *) effp->priv;
70   char * s;
71   char dummy;     /* To check for extraneous chars. */
72   unsigned pairs, i, j, commas;
73 
74   --argc, ++argv;
75   if (argc < 2 || argc > 5)
76     return lsx_usage(effp);
77 
78   l->arg0 = lsx_strdup(argv[0]);
79   l->arg1 = lsx_strdup(argv[1]);
80   l->arg2 = argc > 2 ? lsx_strdup(argv[2]) : NULL;
81 
82   /* Start by checking the attack and decay rates */
83   for (s = l->arg0, commas = 0; *s; ++s) if (*s == ',') ++commas;
84   if ((commas % 2) == 0) {
85     lsx_fail("there must be an even number of attack/decay parameters");
86     return SOX_EOF;
87   }
88   pairs = 1 + commas/2;
89   l->channels = lsx_calloc(pairs, sizeof(*l->channels));
90   l->expectedChannels = pairs;
91 
92   /* Now tokenise the rates string and set up these arrays.  Keep
93      them in seconds at the moment: we don't know the sample rate yet. */
94   for (i = 0, s = strtok(l->arg0, ","); s != NULL; ++i) {
95     for (j = 0; j < 2; ++j) {
96       if (sscanf(s, "%lf %c", &l->channels[i].attack_times[j], &dummy) != 1) {
97         lsx_fail("syntax error trying to read attack/decay time");
98         return SOX_EOF;
99       } else if (l->channels[i].attack_times[j] < 0) {
100         lsx_fail("attack & decay times can't be less than 0 seconds");
101         return SOX_EOF;
102       }
103       s = strtok(NULL, ",");
104     }
105   }
106 
107   if (!lsx_compandt_parse(&l->transfer_fn, l->arg1, l->arg2))
108     return SOX_EOF;
109 
110   /* Set the initial "volume" to be attibuted to the input channels.
111      Unless specified, choose 0dB otherwise clipping will
112      result if the user has seleced a long attack time */
113   for (i = 0; i < l->expectedChannels; ++i) {
114     double init_vol_dB = 0;
115     if (argc > 3 && sscanf(argv[3], "%lf %c", &init_vol_dB, &dummy) != 1) {
116       lsx_fail("syntax error trying to read initial volume");
117       return SOX_EOF;
118     } else if (init_vol_dB > 0) {
119       lsx_fail("initial volume is relative to maximum volume so can't exceed 0dB");
120       return SOX_EOF;
121     }
122     l->channels[i].volume = pow(10., init_vol_dB / 20);
123   }
124 
125   /* If there is a delay, store it. */
126   if (argc > 4 && sscanf(argv[4], "%lf %c", &l->delay, &dummy) != 1) {
127     lsx_fail("syntax error trying to read delay value");
128     return SOX_EOF;
129   } else if (l->delay < 0) {
130     lsx_fail("delay can't be less than 0 seconds");
131     return SOX_EOF;
132   }
133 
134   return SOX_SUCCESS;
135 }
136 
start(sox_effect_t * effp)137 static int start(sox_effect_t * effp)
138 {
139   priv_t * l = (priv_t *) effp->priv;
140   unsigned i, j;
141 
142   lsx_debug("%i input channel(s) expected: actually %i",
143       l->expectedChannels, effp->out_signal.channels);
144   for (i = 0; i < l->expectedChannels; ++i)
145     lsx_debug("Channel %i: attack = %g decay = %g", i,
146         l->channels[i].attack_times[0], l->channels[i].attack_times[1]);
147   if (!lsx_compandt_show(&l->transfer_fn, effp->global_info->plot))
148     return SOX_EOF;
149 
150   /* Convert attack and decay rates using number of samples */
151   for (i = 0; i < l->expectedChannels; ++i)
152     for (j = 0; j < 2; ++j)
153       if (l->channels[i].attack_times[j] > 1.0/effp->out_signal.rate)
154         l->channels[i].attack_times[j] = 1.0 -
155           exp(-1.0/(effp->out_signal.rate * l->channels[i].attack_times[j]));
156       else
157         l->channels[i].attack_times[j] = 1.0;
158 
159   /* Allocate the delay buffer */
160   l->delay_buf_size = l->delay * effp->out_signal.rate * effp->out_signal.channels;
161   if (l->delay_buf_size > 0)
162     l->delay_buf = lsx_calloc((size_t)l->delay_buf_size, sizeof(*l->delay_buf));
163   l->delay_buf_index = 0;
164   l->delay_buf_cnt = 0;
165   l->delay_buf_full= 0;
166 
167   return SOX_SUCCESS;
168 }
169 
170 /*
171  * Update a volume value using the given sample
172  * value, the attack rate and decay rate
173  */
doVolume(double * v,double samp,priv_t * l,int chan)174 static void doVolume(double *v, double samp, priv_t * l, int chan)
175 {
176   double s = -samp / SOX_SAMPLE_MIN;
177   double delta = s - *v;
178 
179   if (delta > 0.0) /* increase volume according to attack rate */
180     *v += delta * l->channels[chan].attack_times[0];
181   else             /* reduce volume according to decay rate */
182     *v += delta * l->channels[chan].attack_times[1];
183 }
184 
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)185 static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
186                     size_t *isamp, size_t *osamp)
187 {
188   priv_t * l = (priv_t *) effp->priv;
189   int len =  (*isamp > *osamp) ? *osamp : *isamp;
190   int filechans = effp->out_signal.channels;
191   int idone,odone;
192 
193   for (idone = 0,odone = 0; idone < len; ibuf += filechans) {
194     int chan;
195 
196     /* Maintain the volume fields by simulating a leaky pump circuit */
197     for (chan = 0; chan < filechans; ++chan) {
198       if (l->expectedChannels == 1 && filechans > 1) {
199         /* User is expecting same compander for all channels */
200         int i;
201         double maxsamp = 0.0;
202         for (i = 0; i < filechans; ++i) {
203           double rect = fabs((double)ibuf[i]);
204           if (rect > maxsamp) maxsamp = rect;
205         }
206         doVolume(&l->channels[0].volume, maxsamp, l, 0);
207         break;
208       } else
209         doVolume(&l->channels[chan].volume, fabs((double)ibuf[chan]), l, chan);
210     }
211 
212     /* Volume memory is updated: perform compand */
213     for (chan = 0; chan < filechans; ++chan) {
214       int ch = l->expectedChannels > 1 ? chan : 0;
215       double level_in_lin = l->channels[ch].volume;
216       double level_out_lin = lsx_compandt(&l->transfer_fn, level_in_lin);
217       double checkbuf;
218 
219       if (l->delay_buf_size <= 0) {
220         checkbuf = ibuf[chan] * level_out_lin;
221         SOX_SAMPLE_CLIP_COUNT(checkbuf, effp->clips);
222         obuf[odone++] = checkbuf;
223         idone++;
224       } else {
225         if (l->delay_buf_cnt >= l->delay_buf_size) {
226           l->delay_buf_full=1; /* delay buffer is now definitely full */
227           checkbuf = l->delay_buf[l->delay_buf_index] * level_out_lin;
228           SOX_SAMPLE_CLIP_COUNT(checkbuf, effp->clips);
229           obuf[odone] = checkbuf;
230           odone++;
231           idone++;
232         } else {
233           l->delay_buf_cnt++;
234           idone++; /* no "odone++" because we did not fill obuf[...] */
235         }
236         l->delay_buf[l->delay_buf_index++] = ibuf[chan];
237         l->delay_buf_index %= l->delay_buf_size;
238       }
239     }
240   }
241 
242   *isamp = idone; *osamp = odone;
243   return (SOX_SUCCESS);
244 }
245 
drain(sox_effect_t * effp,sox_sample_t * obuf,size_t * osamp)246 static int drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
247 {
248   priv_t * l = (priv_t *) effp->priv;
249   size_t chan, done = 0;
250 
251   if (l->delay_buf_full == 0)
252     l->delay_buf_index = 0;
253   while (done+effp->out_signal.channels <= *osamp && l->delay_buf_cnt > 0)
254     for (chan = 0; chan < effp->out_signal.channels; ++chan) {
255       int c = l->expectedChannels > 1 ? chan : 0;
256       double level_in_lin = l->channels[c].volume;
257       double level_out_lin = lsx_compandt(&l->transfer_fn, level_in_lin);
258       obuf[done++] = l->delay_buf[l->delay_buf_index++] * level_out_lin;
259       l->delay_buf_index %= l->delay_buf_size;
260       l->delay_buf_cnt--;
261     }
262   *osamp = done;
263   return l->delay_buf_cnt > 0 ? SOX_SUCCESS : SOX_EOF;
264 }
265 
stop(sox_effect_t * effp)266 static int stop(sox_effect_t * effp)
267 {
268   priv_t * l = (priv_t *) effp->priv;
269 
270   free(l->delay_buf);
271   return SOX_SUCCESS;
272 }
273 
lsx_kill(sox_effect_t * effp)274 static int lsx_kill(sox_effect_t * effp)
275 {
276   priv_t * l = (priv_t *) effp->priv;
277 
278   lsx_compandt_kill(&l->transfer_fn);
279   free(l->channels);
280   free(l->arg0);
281   free(l->arg1);
282   free(l->arg2);
283   return SOX_SUCCESS;
284 }
285 
lsx_compand_effect_fn(void)286 sox_effect_handler_t const * lsx_compand_effect_fn(void)
287 {
288   static sox_effect_handler_t handler = {
289     "compand", compand_usage, SOX_EFF_MCHAN | SOX_EFF_GAIN,
290     getopts, start, flow, drain, stop, lsx_kill, sizeof(priv_t)
291   };
292   return &handler;
293 }
294