Merge branch 'mr/build' into pu
[sox/ew.git] / src / flanger.c
blobd75f1e6ff91cfb17b23736d83c126876ee67b45c
1 /* libSoX effect: Stereo Flanger (c) 2006 robs@users.sourceforge.net
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.
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.
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
18 /* TODO: Slide in the delay at the start? */
20 #include "sox_i.h"
21 #include <string.h>
23 typedef enum {INTERP_LINEAR, INTERP_QUADRATIC} interp_t;
25 #define MAX_CHANNELS 4
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;
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];
44 /* Low Frequency Oscillator */
45 float * lfo;
46 size_t lfo_length;
47 size_t lfo_pos;
49 /* Balancing */
50 double in_gain;
51 } priv_t;
55 static lsx_enum_item const interp_enum[] = {
56 LSX_ENUM_ITEM(INTERP_,LINEAR)
57 LSX_ENUM_ITEM(INTERP_,QUADRATIC)
58 {0, 0}};
62 static int getopts(sox_effect_t * effp, int argc, char *argv[])
64 priv_t * p = (priv_t *) effp->priv;
65 --argc, ++argv;
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;
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);
84 if (argc != 0)
85 return lsx_usage(effp);
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);
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;
112 return SOX_SUCCESS;
117 static int start(sox_effect_t * effp)
119 priv_t * f = (priv_t *) effp->priv;
120 int c, channels = effp->in_signal.channels;
122 if (channels > MAX_CHANNELS) {
123 lsx_fail("Can not operate with more than %i channels", MAX_CHANNELS);
124 return SOX_EOF;
127 /* Balance output: */
128 f->in_gain = 1 / (1 + f->delay_gain);
129 f->delay_gain /= 1 + f->delay_gain;
131 /* Balance feedback loop: */
132 f->delay_gain *= 1 - fabs(f->feedback_gain);
134 lsx_debug("in_gain=%g feedback_gain=%g delay_gain=%g\n",
135 f->in_gain, f->feedback_gain, f->delay_gain);
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]));
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) */
157 lsx_debug("delay_buf_length=%" PRIuPTR " lfo_length=%" PRIuPTR "\n",
158 f->delay_buf_length, f->lfo_length);
160 return SOX_SUCCESS;
165 static int flow(sox_effect_t * effp, sox_sample_t const * ibuf,
166 sox_sample_t * obuf, size_t * isamp, size_t * osamp)
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;
172 *isamp = *osamp = len * channels;
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;
186 in = *ibuf++;
187 f->delay_bufs[c][f->delay_buf_pos] = in + f->delay_last[c] * f->feedback_gain;
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];
194 if (f->interpolation == INTERP_LINEAR)
195 delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay;
196 else /* if (f->interpolation == INTERP_QUADRATIC) */
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;
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);
212 f->lfo_pos = (f->lfo_pos + 1) % f->lfo_length;
215 return SOX_SUCCESS;
220 static int stop(sox_effect_t * effp)
222 priv_t * f = (priv_t *) effp->priv;
223 int c, channels = effp->in_signal.channels;
225 for (c = 0; c < channels; ++c)
226 free(f->delay_bufs[c]);
228 free(f->lfo);
230 memset(f, 0, sizeof(*f));
232 return SOX_SUCCESS;
237 sox_effect_handler_t const * lsx_flanger_effect_fn(void)
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"
272 static char * usage;
273 handler.usage = lsx_usage_lines(&usage, lines, array_length(lines));
274 return &handler;