Finally I've found it...
Some time ago, I've made a convolution reverb (it works but crashes often). The schematic is very hard to read...
It works kind of like this:
Assuming window-size is 64 and FFT size is twice (to leave space for tails - explained forther). I will include C-ish code example for readability.
stage 1 - impulse preprocessing:
1.separate first 64samples. They will be convolved in time-domain. Let's call that a nose.
2. FFT each 64sample segment (with added 64samples of silence) with 128FFT and store them (they will take 4x as much space as original impulse). Let's call that a IMPULSE_STACK
stage 2 - convolution.
To process the convolution in frequency domain, you need to load 64sample chunk of input, so the convolution part is actually 64samples delayed. We've already taken care of that - the frequency domain convolution will use impulse trimmed from first 64samples (which will be convolved via time domain convolver).
IN_ARRAY[256] = input buffer
IN_FFTED = stack of FFTed input buffer. 4x the original impulse length. It will be circular buffer with offset N which will be incremented in 128 steps.
ACCUM[512]= accumulator buffer. This one will accumulate the multiplied spectra. it will then be iFFTed and also read from. Because after iFFT the accumulator will also contain additional 64sample reverb tail, you need two. They will be used alternatively. ALT will determine, which accumulator to use - it will flip between 0 and 256 to switch them after each window.
1. buffer 64samples of input into IN_ARRAY and add 64samples of silence. Don't forget imaginary parts.
Code: Select all
streamin in;
int i=0;
IN_ARRAY[i]=in; //even are REAL
in_ARRAY[i+1]=in; //odd are IMAG
//set the second half of buffer to zero
IN_ARRAY[i+128]=0;
IN_ARRAY[i+129]=0;
//this should be at the very end of code, but I include it here for clarity
i=(i+2) % 256; //circulate i.
2. FFT the IN_ARRAY with 128FFT and push it into the IN FFTED starting at position N.
Code: Select all
// fft(dest,source); -both destination and source are arrays - even elements are REALs odd elements are IMAGs
// hope you know how pointers work in C.
N= (N+256) % impulseLength; //N is circulated around impulse length
dest=IN_FFTED + N; //dest is IN_FFTED offset by N
fft(dest,IN_ARRAY);
3. iterate trough IN_FFTED and multiply it with IMPULSE_STACK. Remember IN_FFTED is ofset by N.
Code: Select all
acc=ACCUM+ALT; //this will switch between accumulator parts for odd and even windows.
ALT=(ALT+256) % 512;
//first empty the accumulator array
for(k=0; k<256; k++)
{
acc[k]=0;
}
for(k=impulseLength-2; k<=0 ; k-=2) //iterate over complex frequency bins
//Note the iteration happens in reverse, to minimize rounding errors (end of impulse will most likely contain smaller values)
{
//load real and imaginary parts from IMPULSE_STACK and IN_FFTED
imp_R=IMPULSE_STACK[k];
imp_I=IMPULSE_STACK[k+1];
in_R=IN_FFTED[ (N+k) % impulseLength ]; // the "%impuseLength" is there to wrap the reading
in_I=IN_FFTED[ (N+k+1) % impulseLength ];
//Do complex multiplication and add the results to the acumulator
acc[k %256]= imp_R*in_R - imp_I * in_I; //real part
acc[k %256]= imp_R*in_I + imp_I * in_R; //imaginary part
}
4. iFFT the accumulator (the part that was collected).
5.read the values from the accumulator at samplerate. Note that since accumulator has two parts (reverb tail from one window should overlap with the start of next window), you need to read from two halves:
Code: Select all
streamout out;
out = ACCUM[i] + ACCUM[i+256]; //only real parts are relevant - imaginary parts are zero anyway.
The attacked schematic implements that, but the processing part is completely written in assembler and hard to read (also names of variables, inputs and outputs are misleading).