ref: 557ba98586f8a42923ae1a0c7ec2d7a208c0185c
dir: /src/reverb.c/
/* * Effect: reverb Copyright (c) 2007 robs@users.sourceforge.net * Algorithm based on freeverb by Jezar @ Dreampoint. * * 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. */ #include "sox_i.h" #include "xmalloc.h" #include <math.h> #include <string.h> #define FLOAT float #define filter_create(p, n) (p)->ptr=Xcalloc((p)->buffer, (p)->size=(size_t)(n)) #define filter_delete(p) free((p)->buffer) #define ADVANCE_PTR(ptr) if (--p->ptr < p->buffer) p->ptr += p->size typedef struct { size_t size; FLOAT * buffer, * ptr; FLOAT store; } filter_t; static FLOAT comb_process(filter_t * p, /* gcc -O2 will inline this */ FLOAT input, FLOAT feedback, FLOAT hf_damping) { FLOAT output = *p->ptr; p->store = output + (p->store - output) * hf_damping; *p->ptr = input + p->store * feedback; ADVANCE_PTR(ptr); return output; } static FLOAT allpass_process(filter_t * p, /* gcc -O2 will inline this */ FLOAT input, FLOAT feedback) { FLOAT output = *p->ptr; *p->ptr = input + output * feedback; ADVANCE_PTR(ptr); return output - input; } static const size_t /* Filter delay lengths in samples (44100Hz sample-rate) */ comb_lengths[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617}, allpass_lengths[] = {225, 341, 441, 556}; #define stereo_adjust 12 typedef struct filter_array { filter_t comb [array_length(comb_lengths)]; filter_t allpass[array_length(allpass_lengths)]; } filter_array_t; static void filter_array_create(filter_array_t * p, double rate, double scale, double offset) { size_t i; double r = rate * (1 / 44100.); for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset) filter_create(&p->comb[i], scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5); for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset) filter_create(&p->allpass[i], r * (allpass_lengths[i] + stereo_adjust * offset) + .5); } static void filter_array_delete(filter_array_t * p) { size_t i; for (i = 0; i < array_length(allpass_lengths); ++i) filter_delete(&p->allpass[i]); for (i = 0; i < array_length(comb_lengths); ++i) filter_delete(&p->comb[i]); } static void filter_array_process(filter_array_t * p, size_t length, FLOAT const * input, FLOAT * output, FLOAT feedback, FLOAT hf_damping, FLOAT gain) { while (length--) { FLOAT out = 0, in = *input++; size_t i = array_length(comb_lengths) - 1; do out += comb_process(p->comb + i, in, feedback, hf_damping); while (i--); i = array_length(allpass_lengths) - 1; do out = allpass_process(p->allpass + i, out, .5f); while (i--); *output++ = out * gain; } } static const size_t block_size = 1024; typedef struct reverb { FLOAT feedback; FLOAT hf_damping; FLOAT gain; size_t delay, num_delay_blocks; FLOAT * * in, * out[2]; filter_array_t chan[2]; } reverb_t; static FLOAT * reverb_create(reverb_t * p, double sample_rate_Hz, double wet_gain_dB, double room_scale, /* % */ double reverberance, /* % */ double hf_damping, /* % */ double pre_delay_ms, FLOAT * * out, double stereo_depth) { size_t i; double scale = room_scale / 100 * .9 + .1; double depth = stereo_depth / 100; double a, b; memset(p, 0, sizeof(*p)); b = -1 / log(1 - .5); a = 100 / (1 + log(1 - .98) * b); b *= a; p->feedback = 1 - exp((reverberance - a) / b); p->hf_damping = hf_damping / 100 * .3 + .2; p->gain = exp(wet_gain_dB / 20 * log(10.)) * .015; p->delay = pre_delay_ms / 1000 * sample_rate_Hz + .5; p->num_delay_blocks = (p->delay + block_size - 1) / block_size; Xcalloc(p->in, 1 + p->num_delay_blocks); for (i = 0; i <= p->num_delay_blocks; ++i) Xcalloc(p->in[i], block_size); for (i = 0; i <= ceil(depth); ++i) { filter_array_create(p->chan + i, sample_rate_Hz, scale, i * depth); out[i] = Xcalloc(p->out[i], block_size); } return p->in[0]; } static void reverb_delete(reverb_t * p) { size_t i; for (i = 0; i < 2 && p->out[i]; ++i) { free(p->out[i]); filter_array_delete(p->chan + i); } for (i = 0; i <= p->num_delay_blocks; ++i) free(p->in[i]); free(p->in); } static FLOAT * reverb_process(reverb_t * p, size_t length) { FLOAT * oldest_in = p->in[p->num_delay_blocks]; size_t len1 = p->delay % block_size; size_t len2 = length - len1, i; for (i = 0; i < 2 && p->out[i]; ++i) { FLOAT * * b = p->in + p->num_delay_blocks; if (len1) filter_array_process(p->chan + i, len1, *b-- + block_size - len1, p->out[i], p->feedback, p->hf_damping, p->gain); filter_array_process(p->chan + i, len2, *b, p->out[i] + len1, p->feedback, p->hf_damping, p->gain); } for (i = p->num_delay_blocks; i; --i) p->in[i] = p->in[i - 1]; return p->in[i] = oldest_in; } /*------------------------------- SoX Wrapper --------------------------------*/ typedef struct priv { double reverberance, hf_damping, pre_delay_ms; double stereo_depth, wet_gain_dB, room_scale; sox_bool wet_only; size_t ichannels, ochannels; struct { reverb_t reverb; FLOAT * dry, * dry1, * wet[2]; } f[2]; } priv_t; assert_static(sizeof(struct priv) <= SOX_MAX_EFFECT_PRIVSIZE, /* else */ EFFECT_PRIVSIZE_too_big); static int getopts(sox_effect_t * effp, int argc, char **argv) { priv_t * p = (priv_t *) effp->priv; p->reverberance = p->hf_damping = 50; /* Set non-zero defaults */ p->stereo_depth = p->room_scale = 100; p->wet_only = argc && (!strcmp(*argv, "-w") || !strcmp(*argv, "--wet-only")) && (--argc, ++argv, sox_true); do { /* break-able block */ NUMERIC_PARAMETER(reverberance, 0, 100) NUMERIC_PARAMETER(hf_damping, 0, 100) NUMERIC_PARAMETER(room_scale, 0, 100) NUMERIC_PARAMETER(stereo_depth, 0, 100) NUMERIC_PARAMETER(pre_delay_ms, 0, 500) NUMERIC_PARAMETER(wet_gain_dB, -10, 10) } while (0); return argc ? sox_usage(effp) : SOX_SUCCESS; } static int start(sox_effect_t * effp) { priv_t * p = (priv_t *) effp->priv; size_t i; p->ichannels = p->ochannels = 1; effp->outinfo.rate = effp->ininfo.rate; if (effp->ininfo.channels > 2 && p->stereo_depth) { sox_warn("stereo-depth not applicable with >2 channels"); p->stereo_depth = 0; } if (effp->ininfo.channels == 1 && p->stereo_depth) effp->outinfo.channels = p->ochannels = 2; else effp->outinfo.channels = effp->ininfo.channels; if (effp->ininfo.channels == 2 && p->stereo_depth) p->ichannels = p->ochannels = 2; else effp->flows = effp->ininfo.channels; for (i = 0; i < p->ichannels; ++i) p->f[i].dry = reverb_create(&p->f[i].reverb, effp->ininfo.rate, p->wet_gain_dB, p->room_scale, p->reverberance, p->hf_damping, p->pre_delay_ms, p->f[i].wet, p->stereo_depth); return sox_effect_set_imin(effp, block_size); } static int flow(sox_effect_t * effp, const sox_ssample_t * ibuf, sox_ssample_t * obuf, sox_size_t * isamp, sox_size_t * osamp) { priv_t * p = (priv_t *) effp->priv; sox_size_t c, i, w; if (*isamp < block_size || *osamp < block_size * p->ochannels / p->ichannels) *isamp = *osamp = 0; else { *osamp = (*isamp = block_size) * p->ochannels / p->ichannels; for (i = 0; i < *isamp; ++i) for (c = 0; c < p->ichannels; ++c) p->f[c].dry[i] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips); for (c = 0; c < p->ichannels; ++c) { p->f[c].dry1 = p->f[c].dry; p->f[c].dry = reverb_process(&p->f[c].reverb, *isamp / p->ichannels); } if (p->ichannels == 2) for (i = 0; i < *isamp; ++i) for (w = 0; w < 2; ++w) { FLOAT x = (1 - p->wet_only) * p->f[w].dry1[i] + .5 * (p->f[0].wet[w][i] + p->f[1].wet[w][i]); *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(x, effp->clips); } else for (i = 0; i < *isamp; ++i) for (w = 0; w < min(2, p->ochannels); ++w) { FLOAT x = (1 - p->wet_only) * p->f[0].dry1[i] + p->f[0].wet[w][i]; *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(x, effp->clips); } } return SOX_SUCCESS; } static int stop(sox_effect_t * effp) { priv_t * p = (priv_t *) effp->priv; size_t i; for (i = 0; i < p->ichannels; ++i) reverb_delete(&p->f[i].reverb); return SOX_SUCCESS; } sox_effect_handler_t const *sox_reverb_effect_fn(void) { static sox_effect_handler_t handler = { "reverb", "[-w|--wet-only]" " [reverberance (50%)" " [HF-damping (50%)" " [room-scale (100%)" " [stereo-depth (100%)" " [pre-delay (0ms)" " [wet-gain (0dB)" "]]]]]]", SOX_EFF_MCHAN, getopts, start, flow, NULL, stop, NULL }; return &handler; }