1 /* libSoX effect: Stereo Flanger   (c) 2006 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 /* TODO: Slide in the delay at the start? */
19 
20 #include "sox_i.h"
21 #include <string.h>
22 
23 typedef enum {INTERP_LINEAR, INTERP_QUADRATIC} interp_t;
24 
25 #define MAX_CHANNELS 4
26 
27 typedef struct {
28   /* Parameters */
29   double     delay_min;
30   double     delay_depth;
31   double     feedback_gain;
32   double     delay_gain;
33   double     speed;
34   lsx_wave_t  wave_shape;
35   double     channel_phase;
36   interp_t   interpolation;
37 
38   /* Delay buffers */
39   double *   delay_bufs[MAX_CHANNELS];
40   size_t  delay_buf_length;
41   size_t  delay_buf_pos;
42   double     delay_last[MAX_CHANNELS];
43 
44   /* Low Frequency Oscillator */
45   float *    lfo;
46   size_t  lfo_length;
47   size_t  lfo_pos;
48 
49   /* Balancing */
50   double     in_gain;
51 } priv_t;
52 
53 
54 
55 static lsx_enum_item const interp_enum[] = {
56   LSX_ENUM_ITEM(INTERP_,LINEAR)
57   LSX_ENUM_ITEM(INTERP_,QUADRATIC)
58   {0, 0}};
59 
60 
61 
getopts(sox_effect_t * effp,int argc,char * argv[])62 static int getopts(sox_effect_t * effp, int argc, char *argv[])
63 {
64   priv_t * p = (priv_t *) effp->priv;
65   --argc, ++argv;
66 
67   /* Set non-zero defaults: */
68   p->delay_depth  = 2;
69   p->delay_gain   = 71;
70   p->speed        = 0.5;
71   p->channel_phase= 25;
72 
73   do { /* break-able block */
74     NUMERIC_PARAMETER(delay_min    , 0  , 30 )
75     NUMERIC_PARAMETER(delay_depth  , 0  , 10 )
76     NUMERIC_PARAMETER(feedback_gain,-95 , 95 )
77     NUMERIC_PARAMETER(delay_gain   , 0  , 100)
78     NUMERIC_PARAMETER(speed        , 0.1, 10 )
79     TEXTUAL_PARAMETER(wave_shape, lsx_get_wave_enum())
80     NUMERIC_PARAMETER(channel_phase, 0  , 100)
81     TEXTUAL_PARAMETER(interpolation, interp_enum)
82   } while (0);
83 
84   if (argc != 0)
85     return lsx_usage(effp);
86 
87   lsx_report("parameters:\n"
88       "delay = %gms\n"
89       "depth = %gms\n"
90       "regen = %g%%\n"
91       "width = %g%%\n"
92       "speed = %gHz\n"
93       "shape = %s\n"
94       "phase = %g%%\n"
95       "interp= %s",
96       p->delay_min,
97       p->delay_depth,
98       p->feedback_gain,
99       p->delay_gain,
100       p->speed,
101       lsx_get_wave_enum()[p->wave_shape].text,
102       p->channel_phase,
103       interp_enum[p->interpolation].text);
104 
105   /* Scale to unity: */
106   p->feedback_gain /= 100;
107   p->delay_gain    /= 100;
108   p->channel_phase /= 100;
109   p->delay_min     /= 1000;
110   p->delay_depth   /= 1000;
111 
112   return SOX_SUCCESS;
113 }
114 
115 
116 
start(sox_effect_t * effp)117 static int start(sox_effect_t * effp)
118 {
119   priv_t * f = (priv_t *) effp->priv;
120   int c, channels = effp->in_signal.channels;
121 
122   if (channels > MAX_CHANNELS) {
123     lsx_fail("Can not operate with more than %i channels", MAX_CHANNELS);
124     return SOX_EOF;
125   }
126 
127   /* Balance output: */
128   f->in_gain = 1 / (1 + f->delay_gain);
129   f->delay_gain  /= 1 + f->delay_gain;
130 
131   /* Balance feedback loop: */
132   f->delay_gain *= 1 - fabs(f->feedback_gain);
133 
134   lsx_debug("in_gain=%g feedback_gain=%g delay_gain=%g\n",
135       f->in_gain, f->feedback_gain, f->delay_gain);
136 
137   /* Create the delay buffers, one for each channel: */
138   f->delay_buf_length =
139     (f->delay_min + f->delay_depth) * effp->in_signal.rate + 0.5;
140   ++f->delay_buf_length;  /* Need 0 to n, i.e. n + 1. */
141   ++f->delay_buf_length;  /* Quadratic interpolator needs one more. */
142   for (c = 0; c < channels; ++c)
143     f->delay_bufs[c] = lsx_calloc(f->delay_buf_length, sizeof(*f->delay_bufs[0]));
144 
145   /* Create the LFO lookup table: */
146   f->lfo_length = effp->in_signal.rate / f->speed;
147   f->lfo = lsx_calloc(f->lfo_length, sizeof(*f->lfo));
148   lsx_generate_wave_table(
149       f->wave_shape,
150       SOX_FLOAT,
151       f->lfo,
152       f->lfo_length,
153       floor(f->delay_min * effp->in_signal.rate + .5),
154       f->delay_buf_length - 2.,
155       3 * M_PI_2);  /* Start the sweep at minimum delay (for mono at least) */
156 
157   lsx_debug("delay_buf_length=%" PRIuPTR " lfo_length=%" PRIuPTR "\n",
158       f->delay_buf_length, f->lfo_length);
159 
160   return SOX_SUCCESS;
161 }
162 
163 
164 
flow(sox_effect_t * effp,sox_sample_t const * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)165 static int flow(sox_effect_t * effp, sox_sample_t const * ibuf,
166     sox_sample_t * obuf, size_t * isamp, size_t * osamp)
167 {
168   priv_t * f = (priv_t *) effp->priv;
169   int c, channels = effp->in_signal.channels;
170   size_t len = (*isamp > *osamp ? *osamp : *isamp) / channels;
171 
172   *isamp = *osamp = len * channels;
173 
174   while (len--) {
175     f->delay_buf_pos =
176       (f->delay_buf_pos + f->delay_buf_length - 1) % f->delay_buf_length;
177     for (c = 0; c < channels; ++c) {
178       double delayed_0, delayed_1;
179       double delayed;
180       double in, out;
181       size_t channel_phase = c * f->lfo_length * f->channel_phase + .5;
182       double delay = f->lfo[(f->lfo_pos + channel_phase) % f->lfo_length];
183       double frac_delay = modf(delay, &delay);
184       size_t int_delay = (size_t)delay;
185 
186       in = *ibuf++;
187       f->delay_bufs[c][f->delay_buf_pos] = in + f->delay_last[c] * f->feedback_gain;
188 
189       delayed_0 = f->delay_bufs[c]
190         [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
191       delayed_1 = f->delay_bufs[c]
192         [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
193 
194       if (f->interpolation == INTERP_LINEAR)
195         delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay;
196       else /* if (f->interpolation == INTERP_QUADRATIC) */
197       {
198         double a, b;
199         double delayed_2 = f->delay_bufs[c]
200           [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
201         delayed_2 -= delayed_0;
202         delayed_1 -= delayed_0;
203         a = delayed_2 *.5 - delayed_1;
204         b = delayed_1 * 2 - delayed_2 *.5;
205         delayed = delayed_0 + (a * frac_delay + b) * frac_delay;
206       }
207 
208       f->delay_last[c] = delayed;
209       out = in * f->in_gain + delayed * f->delay_gain;
210       *obuf++ = SOX_ROUND_CLIP_COUNT(out, effp->clips);
211     }
212     f->lfo_pos = (f->lfo_pos + 1) % f->lfo_length;
213   }
214 
215   return SOX_SUCCESS;
216 }
217 
218 
219 
stop(sox_effect_t * effp)220 static int stop(sox_effect_t * effp)
221 {
222   priv_t * f = (priv_t *) effp->priv;
223   int c, channels = effp->in_signal.channels;
224 
225   for (c = 0; c < channels; ++c)
226     free(f->delay_bufs[c]);
227 
228   free(f->lfo);
229 
230   memset(f, 0, sizeof(*f));
231 
232   return SOX_SUCCESS;
233 }
234 
235 
236 
lsx_flanger_effect_fn(void)237 sox_effect_handler_t const * lsx_flanger_effect_fn(void)
238 {
239   static sox_effect_handler_t handler = {
240     "flanger", NULL, SOX_EFF_MCHAN,
241     getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)};
242   static char const * lines[] = {
243     "[delay depth regen width speed shape phase interp]",
244     "                  .",
245     "                 /|regen",
246     "                / |",
247     "            +--(  |------------+",
248     "            |   \\ |            |   .",
249     "           _V_   \\|  _______   |   |\\ width   ___",
250     "          |   |   ' |       |  |   | \\       |   |",
251     "      +-->| + |---->| DELAY |--+-->|  )----->|   |",
252     "      |   |___|     |_______|      | /       |   |",
253     "      |           delay : depth    |/        |   |",
254     "  In  |                 : interp   '         |   | Out",
255     "  --->+               __:__                  | + |--->",
256     "      |              |     |speed            |   |",
257     "      |              |  ~  |shape            |   |",
258     "      |              |_____|phase            |   |",
259     "      +------------------------------------->|   |",
260     "                                             |___|",
261     "       RANGE DEFAULT DESCRIPTION",
262     "delay   0 30    0    base delay in milliseconds",
263     "depth   0 10    2    added swept delay in milliseconds",
264     "regen -95 +95   0    percentage regeneration (delayed signal feedback)",
265     "width   0 100   71   percentage of delayed signal mixed with original",
266     "speed  0.1 10  0.5   sweeps per second (Hz) ",
267     "shape    --    sin   swept wave shape: sine|triangle",
268     "phase   0 100   25   swept wave percentage phase-shift for multi-channel",
269     "                     (e.g. stereo) flange; 0 = 100 = same phase on each channel",
270     "interp   --    lin   delay-line interpolation: linear|quadratic"
271   };
272   static char * usage;
273   handler.usage = lsx_usage_lines(&usage, lines, array_length(lines));
274   return &handler;
275 }
276