Hi List,

I was wondering if someone might be able to help with a performance issue I am having with ChucK in respect to the use of FFT/IFFT.

I've written a Pulsar Generator and I've added a convolution function to the pulse train of the generator.  I found a previous thread that I've used as a basis for the convolution (see the posting below).  The main change was to replace the following line:
adc => Gain input => FFT fftx => blackhole;

with this one:

SinOsc sin => WinFuncEnv env => Pan2 pan => Gain input => FFT fftx => blackhole;
At rest, the JACK DSP Load is about 2.2%.  When I run the Pulsar Generator at a reasonable rate, the DSP Load jumps to about 3.5% or so without convolution.  When I add the convolution function as below, it quickly jumps to a 100% usage with steady Xruns.  The sample sizes I am using for the SndBuf statement below are somewhere between 18000 and 28000 samples.

Is there a strategy anyone could suggest to achieve greater performance of the convolution function below or are there alternative strategies for implementing convolution in general?  I'd appreciate any advice.

+++

[chuck-users] Daniel: Convolution (FFT version)

Perry R Cook prc at CS.Princeton.EDU
Tue Nov 25 19:09:31 EST 2014
FFT version.

Most efficient, lots of delay.  Could chunk up 
and factor, overlap-add for less delay.  This is
the basic idea tho.


// FFT convolution with static impulse response
// by Perry R. Cook, November 2014
// upsides:  as efficient as it could be, save for 
//           constructing a specific fft convolution chugin
// downsides: minimum delay is length of impulse response + buffers
// fix:      break into pieces and overlap add
//  Other fix:  see filter version using my FIR Filter chugin

// our fixed convolution kernal (impulse response)
SndBuf s => FFT ffth => blackhole;  
"CelloBodyShort.wav" => s.read; // whatever you like (caution of length!!)
2 => int fftSize;
while (fftSize < s.samples()) 
    2 *=> fftSize;           // next highest power of two
fftSize => int windowSize;   // this is windowsize, only apply to signal blocks
windowSize/2 => int hopSize; // this can any whole fraction of windowsize
2 *=> fftSize;               // zero pad by 2x factor (for convolve)
// our input signal, replace adc with anything you like
adc => Gain input => FFT fftx => blackhole;  // input signal
IFFT outy => dac;            // our output
fftSize => ffth.size => fftx.size => outy.size; // sizes
Windowing.hann(windowSize) => fftx.window;
//   <<< s.samples(), fftSize >>>;
windowSize::samp => now;     // load impulse response into h
ffth.upchuck() @=> UAnaBlob H; // spectrum of fixed impulse response
s =< ffth =< blackhole;      // don't need impulse resp signal anymore

complex Z[fftSize/2];
1000 => input.gain;          // fiddle with this how you like/need

while (true)  {
    fftx.upchuck() @=> UAnaBlob X; // spectrum of input signal

    // multiply spectra bin by bin (complex for free!):
    for(0 => int i; i < fftSize/2; i++ ) {
        fftx.cval(i) * H.cval(i) => Z[i];	
    }    
    outy.transform( Z );      // take ifft
    hopSize :: samp => now;   // and do it all again
}

Regards,
Mitch