1 /* libSoX effect: stereo reverberation
2  * Copyright (c) 2007 robs@users.sourceforge.net
3  * Filter design based on freeverb by Jezar at Dreampoint.
4  *
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.
9  *
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.
14  *
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
18  */
19 
20 #include "sox_i.h"
21 #include "fifo.h"
22 
23 #define lsx_zalloc(var, n) var = lsx_calloc(n, sizeof(*var))
24 #define filter_advance(p) if (--(p)->ptr < (p)->buffer) (p)->ptr += (p)->size
25 #define filter_delete(p) free((p)->buffer)
26 
27 typedef struct {
28   size_t  size;
29   float   * buffer, * ptr;
30   float   store;
31 } filter_t;
32 
comb_process(filter_t * p,float const * input,float const * feedback,float const * hf_damping)33 static float comb_process(filter_t * p,  /* gcc -O2 will inline this */
34     float const * input, float const * feedback, float const * hf_damping)
35 {
36   float output = *p->ptr;
37   p->store = output + (p->store - output) * *hf_damping;
38   *p->ptr = *input + p->store * *feedback;
39   filter_advance(p);
40   return output;
41 }
42 
allpass_process(filter_t * p,float const * input)43 static float allpass_process(filter_t * p,  /* gcc -O2 will inline this */
44     float const * input)
45 {
46   float output = *p->ptr;
47   *p->ptr = *input + output * .5;
48   filter_advance(p);
49   return output - *input;
50 }
51 
52 static const size_t /* Filter delay lengths in samples (44100Hz sample-rate) */
53   comb_lengths[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617},
54   allpass_lengths[] = {225, 341, 441, 556};
55 #define stereo_adjust 12
56 
57 typedef struct {
58   filter_t comb   [array_length(comb_lengths)];
59   filter_t allpass[array_length(allpass_lengths)];
60 } filter_array_t;
61 
filter_array_create(filter_array_t * p,double rate,double scale,double offset)62 static void filter_array_create(filter_array_t * p, double rate,
63     double scale, double offset)
64 {
65   size_t i;
66   double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */
67 
68   for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset)
69   {
70     filter_t * pcomb = &p->comb[i];
71     pcomb->size = (size_t)(scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5);
72     pcomb->ptr = lsx_zalloc(pcomb->buffer, pcomb->size);
73   }
74   for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset)
75   {
76     filter_t * pallpass = &p->allpass[i];
77     pallpass->size = (size_t)(r * (allpass_lengths[i] + stereo_adjust * offset) + .5);
78     pallpass->ptr = lsx_zalloc(pallpass->buffer, pallpass->size);
79   }
80 }
81 
filter_array_process(filter_array_t * p,size_t length,float const * input,float * output,float const * feedback,float const * hf_damping,float const * gain)82 static void filter_array_process(filter_array_t * p,
83     size_t length, float const * input, float * output,
84     float const * feedback, float const * hf_damping, float const * gain)
85 {
86   while (length--) {
87     float out = 0, in = *input++;
88 
89     size_t i = array_length(comb_lengths) - 1;
90     do out += comb_process(p->comb + i, &in, feedback, hf_damping);
91     while (i--);
92 
93     i = array_length(allpass_lengths) - 1;
94     do out = allpass_process(p->allpass + i, &out);
95     while (i--);
96 
97     *output++ = out * *gain;
98   }
99 }
100 
filter_array_delete(filter_array_t * p)101 static void filter_array_delete(filter_array_t * p)
102 {
103   size_t i;
104 
105   for (i = 0; i < array_length(allpass_lengths); ++i)
106     filter_delete(&p->allpass[i]);
107   for (i = 0; i < array_length(comb_lengths); ++i)
108     filter_delete(&p->comb[i]);
109 }
110 
111 typedef struct {
112   float feedback;
113   float hf_damping;
114   float gain;
115   fifo_t input_fifo;
116   filter_array_t chan[2];
117   float * out[2];
118 } reverb_t;
119 
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,double stereo_depth,size_t buffer_size,float ** out)120 static void reverb_create(reverb_t * p, double sample_rate_Hz,
121     double wet_gain_dB,
122     double room_scale,     /* % */
123     double reverberance,   /* % */
124     double hf_damping,     /* % */
125     double pre_delay_ms,
126     double stereo_depth,
127     size_t buffer_size,
128     float * * out)
129 {
130   size_t i, delay = pre_delay_ms / 1000 * sample_rate_Hz + .5;
131   double scale = room_scale / 100 * .9 + .1;
132   double depth = stereo_depth / 100;
133   double a =  -1 /  log(1 - /**/.3 /**/);           /* Set minimum feedback */
134   double b = 100 / (log(1 - /**/.98/**/) * a + 1);  /* Set maximum feedback */
135 
136   memset(p, 0, sizeof(*p));
137   p->feedback = 1 - exp((reverberance - b) / (a * b));
138   p->hf_damping = hf_damping / 100 * .3 + .2;
139   p->gain = dB_to_linear(wet_gain_dB) * .015;
140   fifo_create(&p->input_fifo, sizeof(float));
141   memset(fifo_write(&p->input_fifo, delay, 0), 0, delay * sizeof(float));
142   for (i = 0; i <= ceil(depth); ++i) {
143     filter_array_create(p->chan + i, sample_rate_Hz, scale, i * depth);
144     out[i] = lsx_zalloc(p->out[i], buffer_size);
145   }
146 }
147 
reverb_process(reverb_t * p,size_t length)148 static void reverb_process(reverb_t * p, size_t length)
149 {
150   size_t i;
151   for (i = 0; i < 2 && p->out[i]; ++i)
152     filter_array_process(p->chan + i, length, (float *) fifo_read_ptr(&p->input_fifo), p->out[i], &p->feedback, &p->hf_damping, &p->gain);
153   fifo_read(&p->input_fifo, length, NULL);
154 }
155 
reverb_delete(reverb_t * p)156 static void reverb_delete(reverb_t * p)
157 {
158   size_t i;
159   for (i = 0; i < 2 && p->out[i]; ++i) {
160     free(p->out[i]);
161     filter_array_delete(p->chan + i);
162   }
163   fifo_delete(&p->input_fifo);
164 }
165 
166 /*------------------------------- SoX Wrapper --------------------------------*/
167 
168 typedef struct {
169   double reverberance, hf_damping, pre_delay_ms;
170   double stereo_depth, wet_gain_dB, room_scale;
171   sox_bool wet_only;
172 
173   size_t ichannels, ochannels;
174   struct {
175     reverb_t reverb;
176     float * dry, * wet[2];
177   } chan[2];
178 } priv_t;
179 
getopts(sox_effect_t * effp,int argc,char ** argv)180 static int getopts(sox_effect_t * effp, int argc, char **argv)
181 {
182   priv_t * p = (priv_t *)effp->priv;
183   p->reverberance = p->hf_damping = 50; /* Set non-zero defaults */
184   p->stereo_depth = p->room_scale = 100;
185 
186   --argc, ++argv;
187   p->wet_only = argc && (!strcmp(*argv, "-w") || !strcmp(*argv, "--wet-only"))
188     && (--argc, ++argv, sox_true);
189   do {  /* break-able block */
190     NUMERIC_PARAMETER(reverberance, 0, 100)
191     NUMERIC_PARAMETER(hf_damping, 0, 100)
192     NUMERIC_PARAMETER(room_scale, 0, 100)
193     NUMERIC_PARAMETER(stereo_depth, 0, 100)
194     NUMERIC_PARAMETER(pre_delay_ms, 0, 500)
195     NUMERIC_PARAMETER(wet_gain_dB, -10, 10)
196   } while (0);
197 
198   return argc ? lsx_usage(effp) : SOX_SUCCESS;
199 }
200 
start(sox_effect_t * effp)201 static int start(sox_effect_t * effp)
202 {
203   priv_t * p = (priv_t *)effp->priv;
204   size_t i;
205 
206   p->ichannels = p->ochannels = 1;
207   effp->out_signal.rate = effp->in_signal.rate;
208   if (effp->in_signal.channels > 2 && p->stereo_depth) {
209     lsx_warn("stereo-depth not applicable with >2 channels");
210     p->stereo_depth = 0;
211   }
212   if (effp->in_signal.channels == 1 && p->stereo_depth)
213     effp->out_signal.channels = p->ochannels = 2;
214   else effp->out_signal.channels = effp->in_signal.channels;
215   if (effp->in_signal.channels == 2 && p->stereo_depth)
216     p->ichannels = p->ochannels = 2;
217   else effp->flows = effp->in_signal.channels;
218   for (i = 0; i < p->ichannels; ++i) reverb_create(
219     &p->chan[i].reverb, effp->in_signal.rate, p->wet_gain_dB, p->room_scale,
220     p->reverberance, p->hf_damping, p->pre_delay_ms, p->stereo_depth,
221     effp->global_info->global_info->bufsiz / p->ochannels, p->chan[i].wet);
222 
223   if (effp->in_signal.mult)
224     *effp->in_signal.mult /= !p->wet_only + 2 * dB_to_linear(max(0,p->wet_gain_dB));
225   return SOX_SUCCESS;
226 }
227 
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)228 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
229                 sox_sample_t * obuf, size_t * isamp, size_t * osamp)
230 {
231   priv_t * p = (priv_t *)effp->priv;
232   size_t c, i, w, len = min(*isamp / p->ichannels, *osamp / p->ochannels);
233   SOX_SAMPLE_LOCALS;
234 
235   *isamp = len * p->ichannels, *osamp = len * p->ochannels;
236   for (c = 0; c < p->ichannels; ++c)
237     p->chan[c].dry = fifo_write(&p->chan[c].reverb.input_fifo, len, 0);
238   for (i = 0; i < len; ++i) for (c = 0; c < p->ichannels; ++c)
239     p->chan[c].dry[i] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
240   for (c = 0; c < p->ichannels; ++c)
241     reverb_process(&p->chan[c].reverb, len);
242   if (p->ichannels == 2) for (i = 0; i < len; ++i) for (w = 0; w < 2; ++w) {
243     float out = (1 - p->wet_only) * p->chan[w].dry[i] +
244       .5 * (p->chan[0].wet[w][i] + p->chan[1].wet[w][i]);
245     *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(out, effp->clips);
246   }
247   else for (i = 0; i < len; ++i) for (w = 0; w < p->ochannels; ++w) {
248     float out = (1 - p->wet_only) * p->chan[0].dry[i] + p->chan[0].wet[w][i];
249     *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(out, effp->clips);
250   }
251   return SOX_SUCCESS;
252 }
253 
stop(sox_effect_t * effp)254 static int stop(sox_effect_t * effp)
255 {
256   priv_t * p = (priv_t *)effp->priv;
257   size_t i;
258   for (i = 0; i < p->ichannels; ++i)
259     reverb_delete(&p->chan[i].reverb);
260   return SOX_SUCCESS;
261 }
262 
lsx_reverb_effect_fn(void)263 sox_effect_handler_t const *lsx_reverb_effect_fn(void)
264 {
265   static sox_effect_handler_t handler = {"reverb",
266     "[-w|--wet-only]"
267     " [reverberance (50%)"
268     " [HF-damping (50%)"
269     " [room-scale (100%)"
270     " [stereo-depth (100%)"
271     " [pre-delay (0ms)"
272     " [wet-gain (0dB)"
273     "]]]]]]",
274     SOX_EFF_MCHAN, getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)
275   };
276   return &handler;
277 }
278