ref: bda25d62bd2459c9eaa855ca3da8cd91ae1e766a
dir: /src/flanger.c/
/* * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, write to the Free Software Foundation, * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA. */ /* Effect: Stereo Flanger (c) 2006 robs@users.sourceforge.net */ #define sox_flanger_usage \ "[delay depth regen width speed shape phase interp]\n" /* " .\n" \ " /|regen\n" \ " / |\n" \ " +--( |------------+\n" \ " | \\ | | .\n" \ " _V_ \\| _______ | |\\ width ___\n" \ " | | ' | | | | \\ | |\n" \ " +-->| + |---->| DELAY |--+-->| )----->| |\n" \ " | |___| |_______| | / | |\n" \ " | delay : depth |/ | |\n" \ " In | : interp ' | | Out\n" \ " --->+ __:__ | + |--->\n" \ " | | |speed | |\n" \ " | | ~ |shape | |\n" \ " | |_____|phase | |\n" \ " +------------------------------------->| |\n" \ " |___|\n" \ "\n" \ " RANGE DEFAULT DESCRIPTION\n" \ "delay 0 10 0 base delay in milliseconds\n" \ "depth 0 10 2 added swept delay in milliseconds\n" \ "regen -95 +95 0 percentage regeneration (delayed signal feedback)\n" \ "width 0 100 71 percentage of delayed signal mixed with original\n" \ "speed 0.1 10 0.5 sweeps per second (Hz) \n" \ "shape -- sin swept wave shape: sine|triangle\n" \ "phase 0 100 25 swept wave percentage phase-shift for multi-channel\n" \ " (e.g. stereo) flange; 0 = 100 = same phase on each channel\n" \ "interp -- lin delay-line interpolation: linear|quadratic" */ /* TODO: Slide in the delay at the start? */ #include "sox_i.h" #include <math.h> #include <string.h> typedef enum {INTERP_LINEAR, INTERP_QUADRATIC} interp_t; #define MAX_CHANNELS 4 typedef struct flanger { /* Parameters */ double delay_min; double delay_depth; double feedback_gain; double delay_gain; double speed; sox_wave_t wave_shape; double channel_phase; interp_t interpolation; /* Delay buffers */ double * delay_bufs[MAX_CHANNELS]; sox_size_t delay_buf_length; sox_size_t delay_buf_pos; double delay_last[MAX_CHANNELS]; /* Low Frequency Oscillator */ float * lfo; sox_size_t lfo_length; sox_size_t lfo_pos; /* Balancing */ double in_gain; } * flanger_t; assert_static(sizeof(struct flanger) <= SOX_MAX_EFFECT_PRIVSIZE, /* else */ flanger_PRIVSIZE_too_big); static enum_item const interp_enum[] = { ENUM_ITEM(INTERP_,LINEAR) ENUM_ITEM(INTERP_,QUADRATIC) {0, 0}}; #define NUMERIC_PARAMETER(p, min, max) { \ char * end_ptr; \ double d; \ if (argc == 0) break; \ d = strtod(*argv, &end_ptr); \ if (end_ptr != *argv) { \ if (d < min || d > max || *end_ptr != '\0') \ return sox_usage(effp); \ f->p = d; \ --argc, ++argv; \ } \ } #define TEXTUAL_PARAMETER(p, enum_table) { \ enum_item const * e; \ if (argc == 0) break; \ e = find_enum_text(*argv, enum_table); \ if (e != NULL) { \ f->p = e->value; \ --argc, ++argv; \ } \ } static int sox_flanger_getopts(sox_effect_t * effp, int argc, char *argv[]) { flanger_t f = (flanger_t) effp->priv; /* Set non-zero defaults: */ f->delay_depth = 2; f->delay_gain = 71; f->speed = 0.5; f->channel_phase= 25; do { /* break-able block */ NUMERIC_PARAMETER(delay_min , 0 , 10 ) NUMERIC_PARAMETER(delay_depth , 0 , 10 ) NUMERIC_PARAMETER(feedback_gain,-95 , 95 ) NUMERIC_PARAMETER(delay_gain , 0 , 100) NUMERIC_PARAMETER(speed , 0.1, 10 ) TEXTUAL_PARAMETER(wave_shape, sox_wave_enum) NUMERIC_PARAMETER(channel_phase, 0 , 100) TEXTUAL_PARAMETER(interpolation, interp_enum) } while (0); if (argc != 0) return sox_usage(effp); sox_report("parameters:\n" "delay = %gms\n" "depth = %gms\n" "regen = %g%%\n" "width = %g%%\n" "speed = %gHz\n" "shape = %s\n" "phase = %g%%\n" "interp= %s", f->delay_min, f->delay_depth, f->feedback_gain, f->delay_gain, f->speed, sox_wave_enum[f->wave_shape].text, f->channel_phase, interp_enum[f->interpolation].text); return SOX_SUCCESS; } static int sox_flanger_start(sox_effect_t * effp) { flanger_t f = (flanger_t) effp->priv; int c, channels = effp->ininfo.channels; if (channels > MAX_CHANNELS) { sox_fail("Can not operate with more than %i channels", MAX_CHANNELS); return SOX_EOF; } /* Scale percentages to unity: */ f->feedback_gain /= 100; f->delay_gain /= 100; f->channel_phase /= 100; /* Balance output: */ f->in_gain = 1 / (1 + f->delay_gain); f->delay_gain /= 1 + f->delay_gain; /* Balance feedback loop: */ f->delay_gain *= 1 - fabs(f->feedback_gain); sox_debug("in_gain=%g feedback_gain=%g delay_gain=%g\n", f->in_gain, f->feedback_gain, f->delay_gain); /* Create the delay buffers, one for each channel: */ f->delay_buf_length = (f->delay_min + f->delay_depth) / 1000 * effp->ininfo.rate + 0.5; ++f->delay_buf_length; /* Need 0 to n, i.e. n + 1. */ ++f->delay_buf_length; /* Quadratic interpolator needs one more. */ for (c = 0; c < channels; ++c) f->delay_bufs[c] = xcalloc(f->delay_buf_length, sizeof(*f->delay_bufs[0])); /* Create the LFO lookup table: */ f->lfo_length = effp->ininfo.rate / f->speed; f->lfo = xcalloc(f->lfo_length, sizeof(*f->lfo)); sox_generate_wave_table( f->wave_shape, SOX_FLOAT, f->lfo, f->lfo_length, (double)(sox_size_t)(f->delay_min / 1000 * effp->ininfo.rate + .5), (double)(f->delay_buf_length - 2), 3 * M_PI_2); /* Start the sweep at minimum delay (for mono at least) */ sox_debug("delay_buf_length=%u lfo_length=%u\n", f->delay_buf_length, f->lfo_length); return SOX_SUCCESS; } static int sox_flanger_flow(sox_effect_t * effp, sox_ssample_t const * ibuf, sox_ssample_t * obuf, sox_size_t * isamp, sox_size_t * osamp) { flanger_t f = (flanger_t) effp->priv; int c, channels = effp->ininfo.channels; sox_size_t len = (*isamp > *osamp ? *osamp : *isamp) / channels; *isamp = *osamp = len * channels; while (len--) { f->delay_buf_pos = (f->delay_buf_pos + f->delay_buf_length - 1) % f->delay_buf_length; for (c = 0; c < channels; ++c) { double delayed_0, delayed_1; double delayed; double in, out; sox_size_t channel_phase = c * f->lfo_length * f->channel_phase + .5; double delay = f->lfo[(f->lfo_pos + channel_phase) % f->lfo_length]; double frac_delay = modf(delay, &delay); sox_size_t int_delay = (size_t)delay; in = *ibuf++; f->delay_bufs[c][f->delay_buf_pos] = in + f->delay_last[c] * f->feedback_gain; delayed_0 = f->delay_bufs[c] [(f->delay_buf_pos + int_delay++) % f->delay_buf_length]; delayed_1 = f->delay_bufs[c] [(f->delay_buf_pos + int_delay++) % f->delay_buf_length]; if (f->interpolation == INTERP_LINEAR) delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay; else /* if (f->interpolation == INTERP_QUADRATIC) */ { double a, b; double delayed_2 = f->delay_bufs[c] [(f->delay_buf_pos + int_delay++) % f->delay_buf_length]; delayed_2 -= delayed_0; delayed_1 -= delayed_0; a = delayed_2 *.5 - delayed_1; b = delayed_1 * 2 - delayed_2 *.5; delayed = delayed_0 + (a * frac_delay + b) * frac_delay; } f->delay_last[c] = delayed; out = in * f->in_gain + delayed * f->delay_gain; *obuf++ = SOX_ROUND_CLIP_COUNT(out, effp->clips); } f->lfo_pos = (f->lfo_pos + 1) % f->lfo_length; } return SOX_SUCCESS; } static int sox_flanger_stop(sox_effect_t * effp) { flanger_t f = (flanger_t) effp->priv; int c, channels = effp->ininfo.channels; for (c = 0; c < channels; ++c) free(f->delay_bufs[c]); free(f->lfo); memset(f, 0, sizeof(*f)); return SOX_SUCCESS; } static sox_effect_handler_t sox_flanger_effect = { "flanger", sox_flanger_usage, SOX_EFF_MCHAN, sox_flanger_getopts, sox_flanger_start, sox_flanger_flow, NULL, sox_flanger_stop, NULL }; sox_effect_handler_t const * sox_flanger_effect_fn(void) { return &sox_flanger_effect; }