Friday, July 20, 2007

Simplifying Spectral processing in CLAM

Current CLAM Spectrum implementation has four different internal representations: Complex array, polar array, separated magnitude and phase buffers, and separated magnitude and phase break point functions (point controled envelopes). Conversion and synchronization among such representations were handled by the Spectrum class itself and this was intended to be transparent. But processing algorithms had to know which representations were already present to trade off between doing conversion and applying different versions of a given algorithm depending on the representation.

For instance, spectrum product in polar representation is cheaper by itself than complex product, but if you have to convert incoming or outcomming spectrums it migth be not so cheap. What about having two different representations for each operand? Which one should be converted? Output format could be a discriminant but do the module has information to know which is the convenient output representation? Such decissions make the implemention of processings using spectrums really complex.

Besides that, algorithms also had to consider different cases for different scales (linear or dB). So the complexity of processing objects having to deal with spectra is very high. A lot of code dealt with querying the presence of a given representation or synchronizing them.

So, we decided to simplify all that by having several kind of spectrum for each representation and doing any representation change explicit by a converter processing. We are currently considering just two spectrum classes: MagPhaseSpectrum and ComplexSpectrum. We added also converters to the current Spectrum to enable progressive migration. The implementation of FFT and IFFT was highly simplified as we don't need to deal with conversions and prototypes configuration, just to input or output complex spectra.

This is a setup of a system which perform complex and polar products of two spectra:

The spectral product has been simplified. Now we have a product for each kind of spectrum in contrast with the previous situation where we had a single module containing implementations for every combinations of inputs.

Code is really simpler to understand and write this way. After having all that we could do a profiling on comparing which is best, conversions and cheap product or no conversions and expensive product. The option without conversions won but the situation can change on the future depending on the optimizations we could do on the conversion. Having the processing modules factored that way we could change them and doing the benchmark that easy.

In the process of takin such decission we noticed that some elementary CLAM elements such as the AudioSource, AudioSink and specially AudioMixer were taking too much CPU.

After fixing that we got a clear profile that highlights which is the proper strategy for spectrum product for our concrete case. Callgrind is a really nice tool:

So I hope we can move soon to that new way of doing spectral processing in CLAM but still a lot of code must be updated.

No comments: