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
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)
29 float * buffer
, * ptr
;
33 static float comb_process(filter_t
* p
, /* gcc -O2 will inline this */
34 float const * input
, float const * feedback
, float const * hf_damping
)
36 float output
= *p
->ptr
;
37 p
->store
= output
+ (p
->store
- output
) * *hf_damping
;
38 *p
->ptr
= *input
+ p
->store
* *feedback
;
43 static float allpass_process(filter_t
* p
, /* gcc -O2 will inline this */
46 float output
= *p
->ptr
;
47 *p
->ptr
= *input
+ output
* .5;
49 return output
- *input
;
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
58 filter_t comb
[array_length(comb_lengths
)];
59 filter_t allpass
[array_length(allpass_lengths
)];
62 static void filter_array_create(filter_array_t
* p
, double rate
,
63 double scale
, double offset
)
66 double r
= rate
* (1 / 44100.); /* Compensate for actual sample-rate */
68 for (i
= 0; i
< array_length(comb_lengths
); ++i
, offset
= -offset
)
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
);
74 for (i
= 0; i
< array_length(allpass_lengths
); ++i
, offset
= -offset
)
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
);
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
)
87 float out
= 0, in
= *input
++;
89 size_t i
= array_length(comb_lengths
) - 1;
90 do out
+= comb_process(p
->comb
+ i
, &in
, feedback
, hf_damping
);
93 i
= array_length(allpass_lengths
) - 1;
94 do out
= allpass_process(p
->allpass
+ i
, &out
);
97 *output
++ = out
* *gain
;
101 static void filter_array_delete(filter_array_t
* p
)
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
]);
116 filter_array_t chan
[2];
120 static void reverb_create(reverb_t
* p
, double sample_rate_Hz
,
122 double room_scale
, /* % */
123 double reverberance
, /* % */
124 double hf_damping
, /* % */
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 */
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
);
148 static void reverb_process(reverb_t
* p
, size_t length
)
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
);
156 static void reverb_delete(reverb_t
* p
)
159 for (i
= 0; i
< 2 && p
->out
[i
]; ++i
) {
161 filter_array_delete(p
->chan
+ i
);
163 fifo_delete(&p
->input_fifo
);
166 /*------------------------------- SoX Wrapper --------------------------------*/
169 double reverberance
, hf_damping
, pre_delay_ms
;
170 double stereo_depth
, wet_gain_dB
, room_scale
;
173 size_t ichannels
, ochannels
;
176 float * dry
, * wet
[2];
180 static int getopts(sox_effect_t
* effp
, int argc
, char **argv
)
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;
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)
198 return argc
? lsx_usage(effp
) : SOX_SUCCESS
;
201 static int start(sox_effect_t
* effp
)
203 priv_t
* p
= (priv_t
*)effp
->priv
;
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");
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
);
223 if (effp
->in_signal
.mult
)
224 *effp
->in_signal
.mult
/= !p
->wet_only
+ 2 * dB_to_linear(max(0,p
->wet_gain_dB
));
228 static int flow(sox_effect_t
* effp
, const sox_sample_t
* ibuf
,
229 sox_sample_t
* obuf
, size_t * isamp
, size_t * osamp
)
231 priv_t
* p
= (priv_t
*)effp
->priv
;
232 size_t c
, i
, w
, len
= min(*isamp
/ p
->ichannels
, *osamp
/ p
->ochannels
);
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
);
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
);
254 static int stop(sox_effect_t
* effp
)
256 priv_t
* p
= (priv_t
*)effp
->priv
;
258 for (i
= 0; i
< p
->ichannels
; ++i
)
259 reverb_delete(&p
->chan
[i
].reverb
);
263 sox_effect_handler_t
const *lsx_reverb_effect_fn(void)
265 static sox_effect_handler_t handler
= {"reverb",
267 " [reverberance (50%)"
269 " [room-scale (100%)"
270 " [stereo-depth (100%)"
274 SOX_EFF_MCHAN
, getopts
, start
, flow
, NULL
, stop
, NULL
, sizeof(priv_t
)