README.osx wasn't easily readable in Finder. Revert back to
[sox.git] / src / reverb.c
blobcb4460a3662b900020823b05c54dd2cef334d2f6
1 /* libSoX effect: stereo reverberation
2 * Copyright (c) 2007 robs@users.sourceforge.net
3 * Filter design based on freeverb by Jezar at Dreampoint.
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; either version 2.1 of the License, or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "sox_i.h"
21 #include "fifo.h"
23 #define lsx_zalloc(var, n) var = lsx_calloc(n, sizeof(*var))
24 #define filter_create(p, n) (p)->ptr=lsx_zalloc((p)->buffer, (p)->size=(size_t)(n))
25 #define filter_advance(p) if (--(p)->ptr < (p)->buffer) (p)->ptr += (p)->size
26 #define filter_delete(p) free((p)->buffer)
28 typedef struct {
29 size_t size;
30 float * buffer, * ptr;
31 float store;
32 } filter_t;
34 static float comb_process(filter_t * p, /* gcc -O2 will inline this */
35 float const * input, float const * feedback, float const * hf_damping)
37 float output = *p->ptr;
38 p->store = output + (p->store - output) * *hf_damping;
39 *p->ptr = *input + p->store * *feedback;
40 filter_advance(p);
41 return output;
44 static float allpass_process(filter_t * p, /* gcc -O2 will inline this */
45 float const * input)
47 float output = *p->ptr;
48 *p->ptr = *input + output * .5;
49 filter_advance(p);
50 return output - *input;
53 static const size_t /* Filter delay lengths in samples (44100Hz sample-rate) */
54 comb_lengths[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617},
55 allpass_lengths[] = {225, 341, 441, 556};
56 #define stereo_adjust 12
58 typedef struct {
59 filter_t comb [array_length(comb_lengths)];
60 filter_t allpass[array_length(allpass_lengths)];
61 } filter_array_t;
63 static void filter_array_create(filter_array_t * p, double rate,
64 double scale, double offset)
66 size_t i;
67 double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */
69 for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset)
70 filter_create(&p->comb[i], scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5);
71 for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset)
72 filter_create(&p->allpass[i], r * (allpass_lengths[i] + stereo_adjust * offset) + .5);
75 static void filter_array_process(filter_array_t * p,
76 size_t length, float const * input, float * output,
77 float const * feedback, float const * hf_damping, float const * gain)
79 while (length--) {
80 float out = 0, in = *input++;
82 size_t i = array_length(comb_lengths) - 1;
83 do out += comb_process(p->comb + i, &in, feedback, hf_damping);
84 while (i--);
86 i = array_length(allpass_lengths) - 1;
87 do out = allpass_process(p->allpass + i, &out);
88 while (i--);
90 *output++ = out * *gain;
94 static void filter_array_delete(filter_array_t * p)
96 size_t i;
98 for (i = 0; i < array_length(allpass_lengths); ++i)
99 filter_delete(&p->allpass[i]);
100 for (i = 0; i < array_length(comb_lengths); ++i)
101 filter_delete(&p->comb[i]);
104 typedef struct {
105 float feedback;
106 float hf_damping;
107 float gain;
108 fifo_t input_fifo;
109 filter_array_t chan[2];
110 float * out[2];
111 } reverb_t;
113 static void reverb_create(reverb_t * p, double sample_rate_Hz,
114 double wet_gain_dB,
115 double room_scale, /* % */
116 double reverberance, /* % */
117 double hf_damping, /* % */
118 double pre_delay_ms,
119 double stereo_depth,
120 size_t buffer_size,
121 float * * out)
123 size_t i, delay = pre_delay_ms / 1000 * sample_rate_Hz + .5;
124 double scale = room_scale / 100 * .9 + .1;
125 double depth = stereo_depth / 100;
126 double a = -1 / log(1 - /**/.3 /**/); /* Set minimum feedback */
127 double b = 100 / (log(1 - /**/.98/**/) * a + 1); /* Set maximum feedback */
129 memset(p, 0, sizeof(*p));
130 p->feedback = 1 - exp((reverberance - b) / (a * b));
131 p->hf_damping = hf_damping / 100 * .3 + .2;
132 p->gain = dB_to_linear(wet_gain_dB) * .015;
133 fifo_create(&p->input_fifo, sizeof(float));
134 memset(fifo_write(&p->input_fifo, delay, 0), 0, delay * sizeof(float));
135 for (i = 0; i <= ceil(depth); ++i) {
136 filter_array_create(p->chan + i, sample_rate_Hz, scale, i * depth);
137 out[i] = lsx_zalloc(p->out[i], buffer_size);
141 static void reverb_process(reverb_t * p, size_t length)
143 size_t i;
144 for (i = 0; i < 2 && p->out[i]; ++i)
145 filter_array_process(p->chan + i, length, (float *) fifo_read_ptr(&p->input_fifo), p->out[i], &p->feedback, &p->hf_damping, &p->gain);
146 fifo_read(&p->input_fifo, length, NULL);
149 static void reverb_delete(reverb_t * p)
151 size_t i;
152 for (i = 0; i < 2 && p->out[i]; ++i) {
153 free(p->out[i]);
154 filter_array_delete(p->chan + i);
156 fifo_delete(&p->input_fifo);
159 /*------------------------------- SoX Wrapper --------------------------------*/
161 typedef struct {
162 double reverberance, hf_damping, pre_delay_ms;
163 double stereo_depth, wet_gain_dB, room_scale;
164 sox_bool wet_only;
166 size_t ichannels, ochannels;
167 struct {
168 reverb_t reverb;
169 float * dry, * wet[2];
170 } chan[2];
171 } priv_t;
173 static int getopts(sox_effect_t * effp, int argc, char **argv)
175 priv_t * p = (priv_t *)effp->priv;
176 p->reverberance = p->hf_damping = 50; /* Set non-zero defaults */
177 p->stereo_depth = p->room_scale = 100;
179 --argc, ++argv;
180 p->wet_only = argc && (!strcmp(*argv, "-w") || !strcmp(*argv, "--wet-only"))
181 && (--argc, ++argv, sox_true);
182 do { /* break-able block */
183 NUMERIC_PARAMETER(reverberance, 0, 100)
184 NUMERIC_PARAMETER(hf_damping, 0, 100)
185 NUMERIC_PARAMETER(room_scale, 0, 100)
186 NUMERIC_PARAMETER(stereo_depth, 0, 100)
187 NUMERIC_PARAMETER(pre_delay_ms, 0, 500)
188 NUMERIC_PARAMETER(wet_gain_dB, -10, 10)
189 } while (0);
191 return argc ? lsx_usage(effp) : SOX_SUCCESS;
194 static int start(sox_effect_t * effp)
196 priv_t * p = (priv_t *)effp->priv;
197 size_t i;
199 p->ichannels = p->ochannels = 1;
200 effp->out_signal.rate = effp->in_signal.rate;
201 if (effp->in_signal.channels > 2 && p->stereo_depth) {
202 lsx_warn("stereo-depth not applicable with >2 channels");
203 p->stereo_depth = 0;
205 if (effp->in_signal.channels == 1 && p->stereo_depth)
206 effp->out_signal.channels = p->ochannels = 2;
207 else effp->out_signal.channels = effp->in_signal.channels;
208 if (effp->in_signal.channels == 2 && p->stereo_depth)
209 p->ichannels = p->ochannels = 2;
210 else effp->flows = effp->in_signal.channels;
211 for (i = 0; i < p->ichannels; ++i) reverb_create(
212 &p->chan[i].reverb, effp->in_signal.rate, p->wet_gain_dB, p->room_scale,
213 p->reverberance, p->hf_damping, p->pre_delay_ms, p->stereo_depth,
214 effp->global_info->global_info->bufsiz / p->ochannels, p->chan[i].wet);
216 if (effp->in_signal.mult)
217 *effp->in_signal.mult /= !p->wet_only + 2 * dB_to_linear(max(0,p->wet_gain_dB));
218 return SOX_SUCCESS;
221 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
222 sox_sample_t * obuf, size_t * isamp, size_t * osamp)
224 priv_t * p = (priv_t *)effp->priv;
225 size_t c, i, w, len = min(*isamp / p->ichannels, *osamp / p->ochannels);
226 SOX_SAMPLE_LOCALS;
228 *isamp = len * p->ichannels, *osamp = len * p->ochannels;
229 for (c = 0; c < p->ichannels; ++c)
230 p->chan[c].dry = fifo_write(&p->chan[c].reverb.input_fifo, len, 0);
231 for (i = 0; i < len; ++i) for (c = 0; c < p->ichannels; ++c)
232 p->chan[c].dry[i] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
233 for (c = 0; c < p->ichannels; ++c)
234 reverb_process(&p->chan[c].reverb, len);
235 if (p->ichannels == 2) for (i = 0; i < len; ++i) for (w = 0; w < 2; ++w) {
236 float out = (1 - p->wet_only) * p->chan[w].dry[i] +
237 .5 * (p->chan[0].wet[w][i] + p->chan[1].wet[w][i]);
238 *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(out, effp->clips);
240 else for (i = 0; i < len; ++i) for (w = 0; w < p->ochannels; ++w) {
241 float out = (1 - p->wet_only) * p->chan[0].dry[i] + p->chan[0].wet[w][i];
242 *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(out, effp->clips);
244 return SOX_SUCCESS;
247 static int stop(sox_effect_t * effp)
249 priv_t * p = (priv_t *)effp->priv;
250 size_t i;
251 for (i = 0; i < p->ichannels; ++i)
252 reverb_delete(&p->chan[i].reverb);
253 return SOX_SUCCESS;
256 sox_effect_handler_t const *lsx_reverb_effect_fn(void)
258 static sox_effect_handler_t handler = {"reverb",
259 "[-w|--wet-only]"
260 " [reverberance (50%)"
261 " [HF-damping (50%)"
262 " [room-scale (100%)"
263 " [stereo-depth (100%)"
264 " [pre-delay (0ms)"
265 " [wet-gain (0dB)"
266 "]]]]]]",
267 SOX_EFF_MCHAN, getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)
269 return &handler;