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_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)
30 float * buffer
, * ptr
;
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
;
44 static float allpass_process(filter_t
* p
, /* gcc -O2 will inline this */
47 float output
= *p
->ptr
;
48 *p
->ptr
= *input
+ output
* .5;
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
59 filter_t comb
[array_length(comb_lengths
)];
60 filter_t allpass
[array_length(allpass_lengths
)];
63 static void filter_array_create(filter_array_t
* p
, double rate
,
64 double scale
, double offset
)
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
)
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
);
86 i
= array_length(allpass_lengths
) - 1;
87 do out
= allpass_process(p
->allpass
+ i
, &out
);
90 *output
++ = out
* *gain
;
94 static void filter_array_delete(filter_array_t
* p
)
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
]);
109 filter_array_t chan
[2];
113 static void reverb_create(reverb_t
* p
, double sample_rate_Hz
,
115 double room_scale
, /* % */
116 double reverberance
, /* % */
117 double hf_damping
, /* % */
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
)
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
)
152 for (i
= 0; i
< 2 && p
->out
[i
]; ++i
) {
154 filter_array_delete(p
->chan
+ i
);
156 fifo_delete(&p
->input_fifo
);
159 /*------------------------------- SoX Wrapper --------------------------------*/
162 double reverberance
, hf_damping
, pre_delay_ms
;
163 double stereo_depth
, wet_gain_dB
, room_scale
;
166 size_t ichannels
, ochannels
;
169 float * dry
, * wet
[2];
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;
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)
191 return argc
? lsx_usage(effp
) : SOX_SUCCESS
;
194 static int start(sox_effect_t
* effp
)
196 priv_t
* p
= (priv_t
*)effp
->priv
;
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");
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
));
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
);
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
);
247 static int stop(sox_effect_t
* effp
)
249 priv_t
* p
= (priv_t
*)effp
->priv
;
251 for (i
= 0; i
< p
->ichannels
; ++i
)
252 reverb_delete(&p
->chan
[i
].reverb
);
256 sox_effect_handler_t
const *lsx_reverb_effect_fn(void)
258 static sox_effect_handler_t handler
= {"reverb",
260 " [reverberance (50%)"
262 " [room-scale (100%)"
263 " [stereo-depth (100%)"
267 SOX_EFF_MCHAN
, getopts
, start
, flow
, NULL
, stop
, NULL
, sizeof(priv_t
)