Convolution Reverb

#1

I was going to add this to an existing topic, but the system discouraged me from doing so.

I am looking for some words of advice and/or consolation.

I have been trying to create a convolution reverb plugin. I was not aware of and did not use the JUCE convolution class. Instead I used the JUCE FFT engine to roll my own version of fast convolution with overlap add. The good news is that I got it to work (sort of) but I would never foist it on an unsuspecting user. If the IR exceeds a second or maybe two, it cannot meet the hard real time limit. I see the basic problem as this: the FFT size is N + B - 1 rounded up to the next power of 2, where N is the number of IR samples and B is the sample buffer size. Since N >> B, the computation time is dominated by the IR size and is nearly independent of the buffer size. However, it is the buffer size that determines the time limit. I tried combining blocks of samples into superblocks for processing and feeding a queue, but that increases latency and doesn’t really solve the real time problem. I also considered using a separate thread, but that would probably lead to unpredictable and probably bad behavior.

I originally thought that convolution was an impractical approach, but a kindly journal reviewer pointed me to references on “fast convolution”. Well, it may be practical, but with FFT orders of 19 or 20, it seems to be a bit of a stretch.

1 Like

#2

Search the web for partitioned convolution. The easiest to implement form is the uniformly partitioned convolution, where you split Input Signal and IR in equally sized fragments and convolve them. The audio signal already comes in blocks. However, in case the number of samples provided is smaller than blocksize, things get messy. JUCE did a very good job on their convolution which is uniformly partitioned. With the uniformly the highest FFT order is the one you need for twice your blocksize

For longer IRs, non-uniformly partitioned convolution is the most efficient thing to use. It splits the IR in blocks of different sizes. Mostly used is the Gardener scheme: N, 2N, 2N, 4N, 4N, and so on. The idea behind this is, hat you have an additional block of time available to calculate the second block of your IR, and you can even use threads for it. However the scheduling part is the hardest thing to implement.

3 Likes

#3

Thanks. I’ll try re-doing it with the JUCE convolution class.

0 Likes