Audacity 3.2.0
Reverb_libSoX.h
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Reverb_libSoX.h
6 Stereo reverberation effect from libSoX,
7 adapted for Audacity
8
9 Copyright (c) 2007-2013 [email protected]
10 Licence: LGPL v2.1
11 Filter configuration based on freeverb by Jezar Wakefield.
12
13**********************************************************************/
14
15#include <cassert>
16#include <cstring>
17#include <cstdlib>
18#ifdef __WXMSW__
19 #define M_LN10 2.30258509299404568402 /* log_e 10 */
20#else
21 #include <cmath>
22#endif
23#include <algorithm>
24#include <wx/types.h>
25using std::min;
26using std::max;
27
28#define array_length(a) (sizeof(a)/sizeof(a[0]))
29#define dB_to_linear(x) exp((x) * M_LN10 * 0.05)
30#define midi_to_freq(n) (440 * pow(2,((n)-69)/12.))
31#define FIFO_SIZE_T size_t
32#define FIFO_MIN 0x4000
33#define fifo_read_ptr(f) fifo_read(f, (FIFO_SIZE_T)0, NULL)
34#define lsx_zalloc(var, n) var = (float *)calloc(n, sizeof(*var))
35#define filter_advance(p) if (--(p)->ptr < (p)->buffer) (p)->ptr += (p)->size
36#define filter_delete(p) free((p)->buffer)
37
38typedef struct {
39 char * data;
40 size_t allocation; /* Number of bytes allocated for data. */
41 size_t item_size; /* Size of each item in data */
42 size_t begin; /* Offset of the first byte to read. */
43 size_t end; /* 1 + Offset of the last byte to read. */
44} fifo_t;
45
46static void fifo_clear(fifo_t * f)
47{
48 f->end = f->begin = 0;
49}
50
52{
53 f->end = f->begin = 0;
54 memset(f->data, 0, f->allocation);
55}
56
57
58static void * fifo_reserve(fifo_t * f, FIFO_SIZE_T n)
59{
60 n *= f->item_size;
61
62 if (f->begin == f->end)
63 fifo_clear(f);
64
65 while (1) {
66 if (f->end + n <= f->allocation) {
67 void *p = f->data + f->end;
68
69 f->end += n;
70 return p;
71 }
72 if (f->begin > FIFO_MIN) {
73 memmove(f->data, f->data + f->begin, f->end - f->begin);
74 f->end -= f->begin;
75 f->begin = 0;
76 continue;
77 }
78 f->allocation += n;
79 f->data = (char *)realloc(f->data, f->allocation);
80 }
81}
82
83static void * fifo_write(fifo_t * f, FIFO_SIZE_T n, void const * data)
84{
85 void * s = fifo_reserve(f, n);
86 if (data)
87 memcpy(s, data, n * f->item_size);
88 return s;
89}
90
91static void * fifo_read(fifo_t * f, FIFO_SIZE_T n, void * data)
92{
93 char * ret = f->data + f->begin;
94 n *= f->item_size;
95 if (n > (FIFO_SIZE_T)(f->end - f->begin))
96 return NULL;
97 if (data)
98 memcpy(data, ret, (size_t)n);
99 f->begin += n;
100 return ret;
101}
102
103static void fifo_delete(fifo_t * f)
104{
105 free(f->data);
106}
107
108static void fifo_create(fifo_t * f, FIFO_SIZE_T item_size)
109{
110 f->item_size = item_size;
111 f->allocation = FIFO_MIN;
112 f->data = (char *)malloc(f->allocation);
113 fifo_clear(f);
114}
115
116typedef struct {
117 size_t size;
118 float * buffer, * ptr;
119 float store;
120} filter_t;
121
122static float comb_process(filter_t * p, /* gcc -O2 will inline this */
123 float const * input, float const * feedback, float const * hf_damping)
124{
125 float output = *p->ptr;
126 p->store = output + (p->store - output) * *hf_damping;
127 *p->ptr = *input + p->store * *feedback;
129 return output;
130}
131
132static float allpass_process(filter_t * p, /* gcc -O2 will inline this */
133 float const * input)
134{
135 float output = *p->ptr;
136 *p->ptr = *input + output * .5;
138 return output - *input;
139}
140
141typedef struct {double b0, b1, a1, i1, o1;} one_pole_t;
142
143static float one_pole_process(one_pole_t * p, float i0)
144{
145 float o0 = i0*p->b0 + p->i1*p->b1 - p->o1*p->a1;
146 p->i1 = i0;
147 return p->o1 = o0;
148}
149
150static const size_t /* Filter delay lengths in samples (44100Hz sample-rate) */
151 comb_lengths[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617},
152 allpass_lengths[] = {225, 341, 441, 556}, stereo_adjust = 12;
153
154typedef struct {
157 one_pole_t one_pole[2];
159
160static void one_pole_init(filter_array_t* p, double rate,
161 double fc_highpass, double fc_lowpass)
162{
163 { /* EQ: highpass */
164 one_pole_t* q = &p->one_pole[0];
165 q->a1 = -exp(-2 * M_PI * fc_highpass / rate);
166 q->b0 = (1 - q->a1) / 2, q->b1 = -q->b0;
167 }
168 { /* EQ: lowpass */
169 one_pole_t* q = &p->one_pole[1];
170 q->a1 = -exp(-2 * M_PI * fc_lowpass / rate);
171 q->b0 = 1 + q->a1, q->b1 = 0;
172 }
173}
174
175
176static void filter_array_allocate(filter_array_t* p, double rate,
177 double scale, double offset)
178{
179 size_t i;
180 double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */
181
182 for (i = 0; i < array_length(comb_lengths); ++i)
183 {
184 filter_t* pcomb = &p->comb[i];
185 pcomb->size = (size_t)(scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5);
186 pcomb->ptr = lsx_zalloc(pcomb->buffer, pcomb->size);
187 }
188 for (i = 0; i < array_length(allpass_lengths); ++i)
189 {
190 filter_t* pallpass = &p->allpass[i];
191 pallpass->size = (size_t)(r * (allpass_lengths[i] + stereo_adjust * offset) + .5);
192 pallpass->ptr = lsx_zalloc(pallpass->buffer, pallpass->size);
193 }
194}
195
196static void filter_t_resize(filter_t* p, size_t newSize);
197
198static void filter_array_init(filter_array_t* p, double rate,
199 double scale, double offset)
200{
201 size_t i;
202 double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */
203
204 for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset)
205 {
206 filter_t* pcomb = &p->comb[i];
207 size_t newSize = (size_t)(scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5);
208 filter_t_resize(pcomb, newSize);
209 }
210 for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset)
211 {
212 filter_t* pallpass = &p->allpass[i];
213 size_t newSize = (size_t)(r * (allpass_lengths[i] + stereo_adjust * offset) + .5);
214 filter_t_resize(pallpass, newSize);
215 }
216}
217
218
219
220static void filter_array_create(filter_array_t * p, double rate,
221 double scale, double offset)
222{
223 size_t i;
224 double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */
225
226 for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset)
227 {
228 filter_t * pcomb = &p->comb[i];
229 pcomb->size = (size_t)(scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5);
230 pcomb->ptr = lsx_zalloc(pcomb->buffer, pcomb->size);
231 }
232 for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset)
233 {
234 filter_t * pallpass = &p->allpass[i];
235 pallpass->size = (size_t)(r * (allpass_lengths[i] + stereo_adjust * offset) + .5);
236 pallpass->ptr = lsx_zalloc(pallpass->buffer, pallpass->size);
237 }
238}
239
241 size_t length, float const * input, float * output,
242 float const * feedback, float const * hf_damping, float const * gain)
243{
244 while (length--) {
245 float out = 0, in = *input++;
246
247 size_t i = array_length(comb_lengths) - 1;
248 do out += comb_process(p->comb + i, &in, feedback, hf_damping);
249 while (i--);
250
252 do out = allpass_process(p->allpass + i, &out);
253 while (i--);
254
255 out = one_pole_process(&p->one_pole[0], out);
256 out = one_pole_process(&p->one_pole[1], out);
257 *output++ = out * *gain;
258 }
259}
260
262{
263 size_t i;
264
265 for (i = 0; i < array_length(allpass_lengths); ++i)
266 filter_delete(&p->allpass[i]);
267 for (i = 0; i < array_length(comb_lengths); ++i)
268 filter_delete(&p->comb[i]);
269}
270
271typedef struct {
272 float feedback;
274 float gain;
277 float * out[2];
279} reverb_t;
280
281
282// Resizes a filter, resetting its contents
283static void filter_t_resize_resetting(filter_t* p, size_t newSize)
284{
285 memset(p->buffer, 0, newSize * sizeof(float));
286 p->ptr = p->buffer;
287 p->store = 0;
288 p->size = newSize;
289}
290
291
292// Resizes a filter, trying to keep as much of its history as possible.
293static void filter_t_resize_preserving(filter_t* p, size_t newSize)
294{
295 // Imagine we have this filter_t as input:
296 //
297 // ptr goes from right to left and when falling off the left side, it wraps back to the right;
298 // <--ptr-- it reads the most distant sample in the past, and after reading it will write a new sample.
299 // | Imagine someone just recorded "A B C D E", and now we are going to read "A", but a resize request comes.
300 // v
301 // | C B A E D |
302 //
303
304 // Depending on the new requested size and where ptr is, we can have these cases:
305 //
306 // v
307 // | C B A 0 E D | bigger size: right-shift what is right of ptr,
308 // and fill the resulting gap with zeros.
309 //
310 // (with variant: ptr is already at the old right edge - then shift nothing, just append zeros)
311 // v
312 // | C B A D | smaller size, and ptr is within it (but not at the edge): left-shift what is to the right of ptr,
313 // discarding samples.
314 //
315 // V
316 // | C B A | smaller size, and ptr would be at the new right edge; do nothing.
317 //
318 // v
319 // | B A | smaller size, and ptr would be beyond the new right edge: left-shift what is ahead of ptr,
320 // AND move ptr to size-1
321
322 assert(newSize > 0);
323
324 const int sizeDiff = ((ssize_t)newSize - (ssize_t)p->size);
325 const size_t ptrPos = (p->ptr - p->buffer);
326
327 const size_t numSamplesBehindPtr = (p->size - 1) - ptrPos;
328
329 if (sizeDiff > 0)
330 {
331 // case: bigger size
332
333 if (numSamplesBehindPtr > 0)
334 {
335 // right-shift what is right of ptr
336 memcpy(p->ptr + 1 + sizeDiff, p->ptr + 1, numSamplesBehindPtr * sizeof(float));
337 }
338
339 // fill the created gap with zeros
340 memset(p->ptr + 1, 0, sizeDiff * sizeof(float));
341 }
342 else if (sizeDiff < 0)
343 {
344 // case: smaller size
345
346 if (ptrPos < newSize-1)
347 {
348 size_t lenOfBlockToShift = newSize - 1 - ptrPos;
349 float* ptrToBlockToShift = p->buffer + p->size - lenOfBlockToShift;
350 memcpy(p->ptr + 1, ptrToBlockToShift, lenOfBlockToShift * sizeof(float));
351 }
352 else if (ptrPos == newSize - 1)
353 {
354 // sub-case: ptr is at the new edge - no shifting to do, and ptr can stay where it is
355 }
356 else
357 {
358 // sub-case: ptr would be beyond the new edge
359 // left-shift what is ahead of ptr and make ptr point to the new edge
360 memcpy(p->buffer, p->ptr - newSize + 1, newSize * sizeof(float));
361 p->ptr = p->buffer + newSize - 1;
362 }
363 }
364
365 p->size = newSize;
366}
367
368
369static void filter_t_resize(filter_t* p, size_t newSize)
370{
371 // Choose your resize method.
372 //
373 // When moving the Room Size slider:
374 //
375 // - using resize_resetting, you will not hear previous reverberations,
376 // but at the same time you will not hear sound artifacts
377 //
378 // - using resize_preserving, you will hear previous reverberations,
379 // but sometimes you might hear sound artifacts
380
381#if 1
382 filter_t_resize_resetting(p, newSize);
383#else
384 filter_t_resize_preserving(p, newSize);
385#endif
386
387}
388
389
390static void reverb_allocate(reverb_t* p, double sample_rate_Hz, size_t buffer_size, float** out)
391{
392 memset(p, 0, sizeof(*p));
393
394 // Input queue
395 fifo_create(&p->input_fifo, sizeof(float));
396
397 // Outputs
398 out[0] = lsx_zalloc(p->out[0], buffer_size);
399 out[1] = lsx_zalloc(p->out[1], buffer_size);
400
401 // Allpass & Comb filters
402 // pass the numbers that will cause the maximum possible allocation of the
403 // buffer just once, even if it is in excess of what is required by the
404 // settings passed in initialization after the allocation
405 filter_array_allocate(p->chan + 0, sample_rate_Hz, 1.0, 0.0);
406 filter_array_allocate(p->chan + 1, sample_rate_Hz, 1.0, 1.0);
407}
408
409// Some of the params can be set without having to re-init the input fifo
410// or the comb/allpass filters.
412(
413 reverb_t* p,
414 double sample_rate_Hz,
415 double wet_gain_dB,
416 double reverberance,
417 double hf_damping,
418 double tone_low, /* % */
419 double tone_high /* % */
420)
421{
422 // Feedback, Damping, Gain
423 double a = -1 / log(1 - .3 ); /* Set minimum feedback */
424 double b = 100 / (log(1 - .98) * a + 1); /* Set maximum feedback */
425
426 p->feedback = 1 - exp((reverberance - b) / (a * b));
427 p->hf_damping = hf_damping / 100 * .3 + .2;
428 p->gain = dB_to_linear(wet_gain_dB) * .015;
429
430
431 // LP-HP Filters
432 double fc_highpass = midi_to_freq(72 - tone_low / 100 * 48);
433 double fc_lowpass = midi_to_freq(72 + tone_high / 100 * 48);
434 one_pole_init(&p->chan[0], sample_rate_Hz, fc_highpass, fc_lowpass);
435 one_pole_init(&p->chan[1], sample_rate_Hz, fc_highpass, fc_lowpass);
436}
437
438
439static void reverb_init
440(
441 reverb_t* p,
442 double sample_rate_Hz,
443 double wet_gain_dB,
444 double room_scale, /* % */
445 double reverberance,
446 double hf_damping,
447 double pre_delay_ms,
448 double stereo_depth,
449 double tone_low, /* % */
450 double tone_high /* % */
451)
452{
453 // Input queue
455 size_t delay = pre_delay_ms / 1000 * sample_rate_Hz + .5;
456 memset(fifo_write(&p->input_fifo, delay, 0), 0, delay * sizeof(float));
457
458 reverb_set_simple_params(p, sample_rate_Hz, wet_gain_dB, reverberance, hf_damping, tone_low, tone_high);
459
460 // Allpass & Comb filters
461 double scale = room_scale / 100 * .9 + .1;
462 double depth = stereo_depth / 100;
463 for (size_t i = 0; i <= ceil(depth); ++i)
464 {
465 filter_array_init(p->chan + i, sample_rate_Hz, scale, i * depth);
466 }
467
468 // Remember if stereo depth was set to 0, so we do not have to process twice
469 p->initializedWithZeroDepth = (stereo_depth == 0.0);
470
471}
472
473
474static void reverb_create(reverb_t * p, double sample_rate_Hz,
475 double wet_gain_dB,
476 double room_scale, /* % */
477 double reverberance, /* % */
478 double hf_damping, /* % */
479 double pre_delay_ms,
480 double stereo_depth,
481 double tone_low, /* % */
482 double tone_high, /* % */
483 size_t buffer_size,
484 float * * out)
485{
486 reverb_allocate(p, sample_rate_Hz, buffer_size, out);
487
488 reverb_init(p, sample_rate_Hz, wet_gain_dB, room_scale, reverberance, hf_damping, pre_delay_ms, stereo_depth, tone_low, tone_high);
489}
490
491static void reverb_process(reverb_t * p, size_t length)
492{
493 filter_array_process(p->chan, length, (float *) fifo_read_ptr(&p->input_fifo), p->out[0], &p->feedback, &p->hf_damping, &p->gain);
494
496 filter_array_process(p->chan + 1, length, (float*)fifo_read_ptr(&p->input_fifo), p->out[1], &p->feedback, &p->hf_damping, &p->gain);
497
498 fifo_read(&p->input_fifo, length, NULL);
499}
500
501static void reverb_delete(reverb_t * p)
502{
503 size_t i;
504 for (i = 0; i < 2 && p->out[i]; ++i) {
505 free(p->out[i]);
507 }
509}
510
511static void reverb_clear(reverb_t* p)
512{
513 for (size_t c = 0; c < 2; c++)
514 {
515 auto& chn = p->chan[c];
516
517 chn.one_pole[0].i1 = 0.0;
518 chn.one_pole[0].o1 = 0.0;
519
520 chn.one_pole[1].i1 = 0.0;
521 chn.one_pole[1].o1 = 0.0;
522
523 for (size_t combIndex = 0; combIndex < array_length(comb_lengths); combIndex++)
524 {
525 auto& comb = chn.comb[combIndex];
526
527 memset(comb.buffer, 0, comb.size * sizeof(float));
528
529 comb.store = 0.0f;
530 }
531
532 for (size_t allpassIndex = 0; allpassIndex < array_length(allpass_lengths); allpassIndex++)
533 {
534 auto& allpass = chn.allpass[allpassIndex];
535
536 memset(allpass.buffer, 0, allpass.size * sizeof(float));
537
538 allpass.store = 0.0f;
539 }
540 } // loop on channels
541
542 fifo_clear( &(p->input_fifo) );
543}
544
int min(int a, int b)
#define M_PI
Definition: Distortion.cpp:22
static void * fifo_read(fifo_t *f, FIFO_SIZE_T n, void *data)
Definition: Reverb_libSoX.h:91
#define fifo_read_ptr(f)
Definition: Reverb_libSoX.h:33
static float one_pole_process(one_pole_t *p, float i0)
#define midi_to_freq(n)
Definition: Reverb_libSoX.h:30
static void reverb_init(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, double tone_low, double tone_high)
static void * fifo_reserve(fifo_t *f, FIFO_SIZE_T n)
Definition: Reverb_libSoX.h:58
static const size_t comb_lengths[]
static void fifo_delete(fifo_t *f)
static void fifo_create(fifo_t *f, FIFO_SIZE_T item_size)
static const size_t stereo_adjust
#define dB_to_linear(x)
Definition: Reverb_libSoX.h:29
#define FIFO_MIN
Definition: Reverb_libSoX.h:32
static float allpass_process(filter_t *p, float const *input)
static void filter_t_resize_preserving(filter_t *p, size_t newSize)
static void filter_array_create(filter_array_t *p, double rate, double scale, double offset)
static void filter_array_delete(filter_array_t *p)
static void reverb_set_simple_params(reverb_t *p, double sample_rate_Hz, double wet_gain_dB, double reverberance, double hf_damping, double tone_low, double tone_high)
static void 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)
static void reverb_allocate(reverb_t *p, double sample_rate_Hz, size_t buffer_size, float **out)
#define array_length(a)
Definition: Reverb_libSoX.h:28
static void reverb_process(reverb_t *p, size_t length)
static float comb_process(filter_t *p, float const *input, float const *feedback, float const *hf_damping)
static void one_pole_init(filter_array_t *p, double rate, double fc_highpass, double fc_lowpass)
static void reverb_delete(reverb_t *p)
#define filter_delete(p)
Definition: Reverb_libSoX.h:36
static void filter_t_resize(filter_t *p, size_t newSize)
static void reverb_clear(reverb_t *p)
static void 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, double tone_low, double tone_high, size_t buffer_size, float **out)
static const size_t allpass_lengths[]
#define filter_advance(p)
Definition: Reverb_libSoX.h:35
static void fifo_clear_and_zero(fifo_t *f)
Definition: Reverb_libSoX.h:51
static void fifo_clear(fifo_t *f)
Definition: Reverb_libSoX.h:46
#define FIFO_SIZE_T
Definition: Reverb_libSoX.h:31
static void filter_array_allocate(filter_array_t *p, double rate, double scale, double offset)
static void filter_array_init(filter_array_t *p, double rate, double scale, double offset)
static void * fifo_write(fifo_t *f, FIFO_SIZE_T n, void const *data)
Definition: Reverb_libSoX.h:83
static void filter_t_resize_resetting(filter_t *p, size_t newSize)
#define lsx_zalloc(var, n)
Definition: Reverb_libSoX.h:34
void free(void *ptr)
Definition: VectorOps.h:34
size_t begin
Definition: Reverb_libSoX.h:42
size_t item_size
Definition: Reverb_libSoX.h:41
size_t end
Definition: Reverb_libSoX.h:43
char * data
Definition: Reverb_libSoX.h:39
size_t allocation
Definition: Reverb_libSoX.h:40
filter_t comb[array_length(comb_lengths)]
one_pole_t one_pole[2]
filter_t allpass[array_length(allpass_lengths)]
size_t size
float store
float * buffer
float * ptr
fifo_t input_fifo
float * out[2]
float feedback
float hf_damping
float gain
bool initializedWithZeroDepth
filter_array_t chan[2]