1 /* libSoX Echo effect             August 24, 1998
2  *
3  * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
4  * This source code is freely redistributable and may be used for
5  * any purpose.  This copyright notice must be maintained.
6  * Juergen Mueller And Sundry Contributors are not responsible for
7  * the consequences of using this software.
8  *
9  *
10  * Flow diagram scheme for n delays ( 1 <= n <= MAX_ECHOS ):
11  *
12  *                                                    * gain-in  ___
13  * ibuff --+--------------------------------------------------->|   |
14  *         |                                          * decay 1 |   |
15  *         |               +----------------------------------->|   |
16  *         |               |                          * decay 2 | + |
17  *         |               |             +--------------------->|   |
18  *         |               |             |            * decay n |   |
19  *         |    _________  |  _________  |     _________   +--->|___|
20  *         |   |         | | |         | |    |         |  |      |
21  *         +-->| delay 1 |-+-| delay 2 |-+...-| delay n |--+      | * gain-out
22  *             |_________|   |_________|      |_________|         |
23  *                                                                +----->obuff
24  * Usage:
25  *   echos gain-in gain-out delay-1 decay-1 [delay-2 decay-2 ... delay-n decay-n]
26  *
27  * Where:
28  *   gain-in, decay-1 ... decay-n :  0.0 ... 1.0      volume
29  *   gain-out :  0.0 ...      volume
30  *   delay-1 ... delay-n :  > 0.0 msec
31  *
32  * Note:
33  *   when decay is close to 1.0, the samples can begin clipping and the output
34  *   can saturate!
35  *
36  * Hint:
37  *   1 / out-gain > gain-in ( 1 + decay-1 + ... + decay-n )
38  *
39  */
40 
41 #include "sox_i.h"
42 
43 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
44 
45 #define DELAY_BUFSIZ ( 50 * 50U * 1024 )
46 #define MAX_ECHOS 7     /* 24 bit x ( 1 + MAX_ECHOS ) = */
47                         /* 24 bit x 8 = 32 bit !!!      */
48 
49 /* Private data for SKEL file */
50 typedef struct {
51         int     counter[MAX_ECHOS];
52         int     num_delays;
53         double  *delay_buf;
54         float   in_gain, out_gain;
55         float   delay[MAX_ECHOS], decay[MAX_ECHOS];
56         ptrdiff_t samples[MAX_ECHOS], pointer[MAX_ECHOS];
57         size_t sumsamples;
58 } priv_t;
59 
60 /* Private data for SKEL file */
61 
62 /*
63  * Process options
64  */
sox_echos_getopts(sox_effect_t * effp,int argc,char ** argv)65 static int sox_echos_getopts(sox_effect_t * effp, int argc, char **argv)
66 {
67         priv_t * echos = (priv_t *) effp->priv;
68         int i;
69 
70         echos->num_delays = 0;
71 
72   --argc, ++argv;
73         if ((argc < 4) || (argc % 2))
74           return lsx_usage(effp);
75 
76         i = 0;
77         sscanf(argv[i++], "%f", &echos->in_gain);
78         sscanf(argv[i++], "%f", &echos->out_gain);
79         while (i < argc) {
80                 /* Linux bug and it's cleaner. */
81                 sscanf(argv[i++], "%f", &echos->delay[echos->num_delays]);
82                 sscanf(argv[i++], "%f", &echos->decay[echos->num_delays]);
83                 echos->num_delays++;
84                 if ( echos->num_delays > MAX_ECHOS )
85                 {
86                         lsx_fail("echos: to many delays, use less than %i delays",
87                                 MAX_ECHOS);
88                         return (SOX_EOF);
89                 }
90         }
91         echos->sumsamples = 0;
92         return (SOX_SUCCESS);
93 }
94 
95 /*
96  * Prepare for processing.
97  */
sox_echos_start(sox_effect_t * effp)98 static int sox_echos_start(sox_effect_t * effp)
99 {
100         priv_t * echos = (priv_t *) effp->priv;
101         int i;
102         float sum_in_volume;
103         unsigned long j;
104 
105         if ( echos->in_gain < 0.0 )
106         {
107                 lsx_fail("echos: gain-in must be positive!");
108                 return (SOX_EOF);
109         }
110         if ( echos->in_gain > 1.0 )
111         {
112                 lsx_fail("echos: gain-in must be less than 1.0!");
113                 return (SOX_EOF);
114         }
115         if ( echos->out_gain < 0.0 )
116         {
117                 lsx_fail("echos: gain-in must be positive!");
118                 return (SOX_EOF);
119         }
120         for ( i = 0; i < echos->num_delays; i++ ) {
121                 echos->samples[i] = echos->delay[i] * effp->in_signal.rate / 1000.0;
122                 if ( echos->samples[i] < 1 )
123                 {
124                     lsx_fail("echos: delay must be positive!");
125                     return (SOX_EOF);
126                 }
127                 if ( echos->samples[i] > (ptrdiff_t)DELAY_BUFSIZ )
128                 {
129                         lsx_fail("echos: delay must be less than %g seconds!",
130                                 DELAY_BUFSIZ / effp->in_signal.rate );
131                         return (SOX_EOF);
132                 }
133                 if ( echos->decay[i] < 0.0 )
134                 {
135                     lsx_fail("echos: decay must be positive!" );
136                     return (SOX_EOF);
137                 }
138                 if ( echos->decay[i] > 1.0 )
139                 {
140                     lsx_fail("echos: decay must be less than 1.0!" );
141                     return (SOX_EOF);
142                 }
143                 echos->counter[i] = 0;
144                 echos->pointer[i] = echos->sumsamples;
145                 echos->sumsamples += echos->samples[i];
146         }
147         echos->delay_buf = lsx_malloc(sizeof (double) * echos->sumsamples);
148         for ( j = 0; j < echos->sumsamples; ++j )
149                 echos->delay_buf[j] = 0.0;
150         /* Be nice and check the hint with warning, if... */
151         sum_in_volume = 1.0;
152         for ( i = 0; i < echos->num_delays; i++ )
153                 sum_in_volume += echos->decay[i];
154         if ( sum_in_volume * echos->in_gain > 1.0 / echos->out_gain )
155                 lsx_warn("echos: warning >>> gain-out can cause saturation of output <<<");
156 
157   effp->out_signal.length = SOX_UNKNOWN_LEN; /* TODO: calculate actual length */
158 
159         return (SOX_SUCCESS);
160 }
161 
162 /*
163  * Processed signed long samples from ibuf to obuf.
164  * Return number of samples processed.
165  */
sox_echos_flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)166 static int sox_echos_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
167                 size_t *isamp, size_t *osamp)
168 {
169         priv_t * echos = (priv_t *) effp->priv;
170         int j;
171         double d_in, d_out;
172         sox_sample_t out;
173         size_t len = min(*isamp, *osamp);
174         *isamp = *osamp = len;
175 
176         while (len--) {
177                 /* Store delays as 24-bit signed longs */
178                 d_in = (double) *ibuf++ / 256;
179                 /* Compute output first */
180                 d_out = d_in * echos->in_gain;
181                 for ( j = 0; j < echos->num_delays; j++ ) {
182                         d_out += echos->delay_buf[echos->counter[j] + echos->pointer[j]] * echos->decay[j];
183                 }
184                 /* Adjust the output volume and size to 24 bit */
185                 d_out = d_out * echos->out_gain;
186                 out = SOX_24BIT_CLIP_COUNT((sox_sample_t) d_out, effp->clips);
187                 *obuf++ = out * 256;
188                 /* Mix decay of delays and input */
189                 for ( j = 0; j < echos->num_delays; j++ ) {
190                         if ( j == 0 )
191                                 echos->delay_buf[echos->counter[j] + echos->pointer[j]] = d_in;
192                         else
193                                 echos->delay_buf[echos->counter[j] + echos->pointer[j]] =
194                                    echos->delay_buf[echos->counter[j-1] + echos->pointer[j-1]] + d_in;
195                 }
196                 /* Adjust the counters */
197                 for ( j = 0; j < echos->num_delays; j++ )
198                         echos->counter[j] =
199                            ( echos->counter[j] + 1 ) % echos->samples[j];
200         }
201         /* processed all samples */
202         return (SOX_SUCCESS);
203 }
204 
205 /*
206  * Drain out reverb lines.
207  */
sox_echos_drain(sox_effect_t * effp,sox_sample_t * obuf,size_t * osamp)208 static int sox_echos_drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
209 {
210         priv_t * echos = (priv_t *) effp->priv;
211         double d_in, d_out;
212         sox_sample_t out;
213         int j;
214         size_t done;
215 
216         done = 0;
217         /* drain out delay samples */
218         while ( ( done < *osamp ) && ( done < echos->sumsamples ) ) {
219                 d_in = 0;
220                 d_out = 0;
221                 for ( j = 0; j < echos->num_delays; j++ ) {
222                         d_out += echos->delay_buf[echos->counter[j] + echos->pointer[j]] * echos->decay[j];
223                 }
224                 /* Adjust the output volume and size to 24 bit */
225                 d_out = d_out * echos->out_gain;
226                 out = SOX_24BIT_CLIP_COUNT((sox_sample_t) d_out, effp->clips);
227                 *obuf++ = out * 256;
228                 /* Mix decay of delays and input */
229                 for ( j = 0; j < echos->num_delays; j++ ) {
230                         if ( j == 0 )
231                                 echos->delay_buf[echos->counter[j] + echos->pointer[j]] = d_in;
232                         else
233                                 echos->delay_buf[echos->counter[j] + echos->pointer[j]] =
234                                    echos->delay_buf[echos->counter[j-1] + echos->pointer[j-1]];
235                 }
236                 /* Adjust the counters */
237                 for ( j = 0; j < echos->num_delays; j++ )
238                         echos->counter[j] =
239                            ( echos->counter[j] + 1 ) % echos->samples[j];
240                 done++;
241                 echos->sumsamples--;
242         };
243         /* samples played, it remains */
244         *osamp = done;
245         if (echos->sumsamples == 0)
246             return SOX_EOF;
247         else
248             return SOX_SUCCESS;
249 }
250 
251 /*
252  * Clean up echos effect.
253  */
sox_echos_stop(sox_effect_t * effp)254 static int sox_echos_stop(sox_effect_t * effp)
255 {
256         priv_t * echos = (priv_t *) effp->priv;
257 
258         free(echos->delay_buf);
259         echos->delay_buf = NULL;
260         return (SOX_SUCCESS);
261 }
262 
263 static sox_effect_handler_t sox_echos_effect = {
264   "echos",
265   "gain-in gain-out delay decay [ delay decay ... ]",
266   SOX_EFF_LENGTH | SOX_EFF_GAIN,
267   sox_echos_getopts,
268   sox_echos_start,
269   sox_echos_flow,
270   sox_echos_drain,
271   sox_echos_stop,
272   NULL, sizeof(priv_t)
273 };
274 
lsx_echos_effect_fn(void)275 const sox_effect_handler_t *lsx_echos_effect_fn(void)
276 {
277     return &sox_echos_effect;
278 }
279