How does minimum-latency partitioned convolution reverb work when you receive input samples in chunks, rather than one at a time?

I'm writing a reverb system where I receive an input block of samples 480 elements long, do some operation on them, and pass the block on to the next effect.

I've been reading up on partitioned convolution reverb, and it seems to operate on the principle that you just accumulate samples until you have enough to convolve a short block of the impulse while the rest of the input comes in, getting bigger and bigger every chunk. How does this work if I can only start working on the input once it's all accumulated? How do convolution reverb plugins in DAWs work with this limitation (buffer sizes are usually lower, but never single-sample)?

For example, my impulse is 16384 samples long. I partition it into 480 + 32, then 480 + 64, 480 + 128, 480 + 256 (rounded up to 1024), etc. All the partitions add up to a length longer than it would have been if I'd just done 16384 + 480 samples (rounded up to 32768).

In my specific case, the source audio is a sample being played back at variable speed so I guess I could apply the reverb as each sample is being added to the buffer or even bake the reverb if I don't mind the room characteristics shifting with the pitch, but I'd like to understand how others do it first.

This is typically done using a segmented overlap add method or sometimes also refered to as a block convolver.

Let's assume your block size if 512 (makes the numbers a little easier).

1. Chop up your impulse response into 32 blocks of 512 samples each. Zero pad each block to 1024 samples and FFT. You know have 32 filters $$H_0(z) ... H_{31}(z)$$
2. On each new input block: zero pad to 1024 samples and FFT. Keep the most recent 32 signal spectra around, so you have $$X_N(z), X_{N-1}, ... X_{N-31}(z)$$
3. Multiply the spectra and sum them, $$Y(z) = \sum_{k=0}^{31} H_k(z) \cdot X_{N-k}(z)$$
4. Inverse FFT the result: you get 1024 time samples. The last 512 are the "overlap". Store for the next block.
5. Take the first 512 samples of your inverse FFT and add the overlap from the previous block. That's your output.

Repeat steps 2-5 for each new block.

• Thanks! The papers I read say this incurs a latency of N samples, but I'm guessing I was already incurring that latency by processing everything in blocks anyways? – Ott Sep 17 '19 at 1:09
• Depends on what you mean by "N". If you do block based processing, you always incur at least one block of latency no matter what you do. This algorithm doesn't incur more or less latency than any other algorithm that is done block based – Hilmar Sep 17 '19 at 16:20
• Okay, so I've got everything set up, but I'm getting this ugly glitch every buffer. I thought at first that it was because I wasn't trimming off the second half of the inverse fft (circular convolution...) and doing that seems to have made the effect more pronounced (though I'm pretty sure that's the correct thing to do). Could it be that I need to be using a smooth windowing function or something? Here's an audio clip of a 440hz sine ping being convolved with a 32 sample triangle from 0 to 1: we.tl/t-KM0RUbRLBg – Ott Sep 19 '19 at 1:49
• No window necessary if you handle the overlap correctly and properly zero pad. You need to add the the second half from IFFT buffer N-1 to the first half of buffer IFFT buffer N. Read up on "overlap add" convolution – Hilmar Sep 20 '19 at 14:08