Little confusion on filtering channels correctly


#1

Hi!

I have a question with a (no doubt) rather simple answer, but I want to make sure I’m doing this correctly. I’m simply trying to filter my left and right channels with two identical IIR filters (i.e one for each), but I’m not certain the way I have implemented it is correct.

Currently, I’m using:

 for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
		if (channel==0)
			filter_l.processSamples(buffer.getWritePointer(channel), buffer.getNumSamples());
		if (channel==1)
			filter_r.processSamples(buffer.getWritePointer(channel), buffer.getNumSamples());
}

(I know the if statements aren’t necessary if I use an array to hold the filters, but I’m just using that at the moment for convenience).

I think my issue lies with not knowing exactly what “buffer.getWritePointer(channel)” is doing or pointing to, or more generally how exactly the buffer works (especially when there are multiple channels). Is it like one long array, where the first half corresponds to the first channel, and second half the second? Or are the samples interleaved?

I will eventually want to have this operating not directly on the input buffer but on a copy of the buffer or something like that, so I have the unadultered input and the filtered input both available to use for further processing. Is making a copy of the buffer the easiest way to do this, or is there a different method in the IIR class that returns the filtered buffer instead of operating on it directly?

Thanks for any help!


#2

The AudioBuffer class is a wrapper around a 2D array where each index holds a buffer of samples (it’s not interleaved).

getWritePointer returns a pointer to the first index of the buffer for a given channel.

If you want to have intermediate processing you need to allocate your own data before the audio callback (never allocate in the audio thread). To do that, construct whatever intermediary buffers you need in prepareToPlay where you are passed the maximum possible buffersize that will be used during the process callback.


#3

Thanks for your help! That cleared up a lot. I’m trying to use intermediate buffers now, but my output is fully of continuous clicks and other nastiness, so I’m no doubt messing it up. What I’ve tried, is defining a buffer in prepareToPlay:

BufferCopy1.setSize(getNumInputChannels(), samplesPerBlock);

Then in my process block:

// Copy each channel in the input buffer into Buffercopy1
for (int i = 0; i<buffer.getNumChannels(); i++)
	BufferCopy1.copyFrom(i, 0, buffer.getReadPointer(i), buffer.getNumSamples());

// Now perform the normal processing but on BufferCopy1

// Copy bufferCopy1 back into final buffer
for (int i = 0; i<BufferCopy1.getNumChannels(); i++)
	buffer.copyFrom(i, 0, BufferCopy1.getReadPointer(i), BufferCopy1.getNumSamples());

So at the moment I’m trying to make it function exactly as it does currently, just using a copy of the buffer. I’m guessing I’m copying to the ‘final out buffer’ incorrectly, perhaps?

Thanks for any help!


#4

The thing to mention here is, that the host is not required to give you the same number of samples each time processBlock is called.
But it is required never to exceed the blocksize given in prepareToPlay. So most probably the buffer you allocated in prepareToPlay is larger than the processed one.

What you do, is make sure when copying back to use the same number for numSamples. I usually have an extra local variable that I use for all copy statements, so I don’t get confused:

const auto numSamples = buffer.getNumSamples();

for (int i = 0; i<BufferCopy1.getNumChannels(); i++)
	buffer.copyFrom (i, 0, BufferCopy1.getReadPointer(i), numSamples);

HTH


#5

By the way, there’s no need here to call getReadPointer. The AudioBuffer has a
copyFrom (int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples)
function which allows you to pass in the other audio buffer.

Note: This won’t cause the problems here, however its nice to use as few raw pointers as possible, so I’d prefer this call over the one you have chosen.


#6

Thanks guys - that was indeed the problem (Duh! I should’ve noticed that). And thanks for the tip, PluginPenguin, I’ll use that instead. The final code, for anyone else with a similar issue:

prepareToPlay:

BufferCopy1.setSize(getNumInputChannels(), samplesPerBlock, false, true);

ProcessBlock:

int BufferSamples = buffer.getNumSamples();

// Copy of the input buffer.
for (int i = 0; i<buffer.getNumChannels(); i++)
	BufferCopy1.copyFrom(i, 0, buffer, i, 0, BufferSamples);

// Do processing on BufferCopy1 (remember to use BufferSamples, though)

// Copy into final buffer
for (int i = 0; i < BufferCopy1.getNumChannels(); i++)
	buffer.copyFrom(i, 0, BufferCopy1, i, 0, BufferSamples);

Thanks!