AudioSampleBuffer memory layout and 2D FFT


#1

Hi all

I’m developing multi-channel (up to 16 input) VST plug-ins.To reduce the amount of processing required I’m trying to compute a 2D FFT of all channels simultaneously, rather than a separate FFT for each, using the FFTW library.

My understanding is that each channel of the AudioSampleBuffer is defined as a separate block of contiguous memory, but the 2D FFTW transform requires one large block of contiguous memory for concurrent audio channels in row-major format. Optimistically I tried the starting pointer of the AudioSampleBuffer (using &audioBuffer.getArrayOfChannels()[0][0] and audioBuffer.getSampleData(0)) but unsurprisingly it isn’t giving the desired results. Has anybody got any direct experience with this kind of thing? Or just some general information about the memory layout of AudioSampleBuffer, and if (with some pointer trickery) it is possible to consider it as one block of memory ?

Thanks.


#2

Lots of experience. Routines to manipulate buffers of audio data, including interleaving and de-interleaving, are in my DSP Filters library:

You want the routines in “Utilities.h”:

// Interleave separate channels from source pointers to destination // (Destination requires channels*frames samples of storage). Performs // implicit type conversion. template <typename Td, typename Ts> void interleave (int channels, size_t samples, Td* dest, Ts const* const* src)

and

// Deinterleave channels. Performs implicit type conversion. template <typename Td, typename Ts> void deinterleave (int channels, int samples, Td* const* dest, Ts const* src)


#3

Ah thanks your code looks useful, so the answer is that I have to copy/interleave the data from all channels to another array elsewhere before running the 2D FFT. Is this going to offer significant savings over running a 1D FFT for every channel straight from AudioSampleBuffer?


#4

Looking inside AudioSampleBuffer::allocateData() we can see that the sample data is in fact one large HeapBlock.

allocatedBytes = (int) (numChannels * size * sizeof (float) + channelListSize + 32); allocatedData.malloc (allocatedBytes);

So getting a pointer to the first channel, getSampleData (0) should give you a pointer to the block of data with the second channel starting at “getSampleData (0) + getNumSamples()” etc. Isn’t this what you want? No need to interleave/deinterleave (Juce has these capabilities by the way see AudioDataConvertors).

Perhaps I am missing row-major format but I though it meant samples laid out like this:

int A[2][3] = { {1, 2, 3}, {4, 5, 6} }; in memory: 1, 2, 3, 4, 5, 6


#5

Now that’s what I was looking for, thanks. I realised afterwards that interleaving wasn’t required, but from your post it seems like I’ve been moving data around into the format it was to start with :roll:

Can’t explain why the 2D FFT isn’t working though when I pass it that first pointer, I’ll reread the fftw documentation and see if I can figure out where I’m going wrong.