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? */
23 typedef enum {INTERP_LINEAR
, INTERP_QUADRATIC
} interp_t
;
25 #define MAX_CHANNELS 4
34 lsx_wave_t wave_shape
;
36 interp_t interpolation
;
39 double * delay_bufs
[MAX_CHANNELS
];
40 size_t delay_buf_length
;
42 double delay_last
[MAX_CHANNELS
];
44 /* Low Frequency Oscillator */
55 static lsx_enum_item
const interp_enum
[] = {
56 LSX_ENUM_ITEM(INTERP_
,LINEAR
)
57 LSX_ENUM_ITEM(INTERP_
,QUADRATIC
)
62 static int getopts(sox_effect_t
* effp
, int argc
, char *argv
[])
64 priv_t
* p
= (priv_t
*) effp
->priv
;
67 /* Set non-zero defaults: */
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
)
85 return lsx_usage(effp
);
87 lsx_report("parameters:\n"
101 lsx_get_wave_enum()[p
->wave_shape
].text
,
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;
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
);
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(
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
);
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
;
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
;
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
;
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) */
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
;
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
]);
230 memset(f
, 0, sizeof(*f
));
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]",
247 " +--( |------------+",
249 " _V_ \\| _______ | |\\ width ___",
250 " | | ' | | | | \\ | |",
251 " +-->| + |---->| DELAY |--+-->| )----->| |",
252 " | |___| |_______| | / | |",
253 " | delay : depth |/ | |",
254 " In | : interp ' | | Out",
255 " --->+ __:__ | + |--->",
258 " | |_____|phase | |",
259 " +------------------------------------->| |",
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"
273 handler
.usage
= lsx_usage_lines(&usage
, lines
, array_length(lines
));