Hello everyone,
I’ve just started getting into JUCE (with a fairly decent DSP and C++ background), and as I’ve seen others try, I want to build a basic biquad filter (I know the IIR object exists, but I want to build this from scratch to get familiar with the way JUCE works).
Anyway, the idea seems pretty simple: keep a history of input values and output values so that I have the delayed inputs and outputs needed for the difference equation calculation. I could do this only storing the two values outside the new input block for each new block, but to keep things clear in my head I created x_hist(N+2) (input) and y_hist(N+2) (output) AudioSampleBuffers (with N being the blocksize) to contain this data and pass it to/from the mainInputOutput Buffer.
Problem is, I can’t get it to work right, and have been debugging for hours without success, going through many similar topics on the forum… basically, in the processBlock method the values [0,1] of the previous input block are copied to [N,N+1], the new input buffer data is then stored into [0,N-1], which should ensure a stream of N+2 samples of coherent data needed for the difference equation…
The strange thing is that the copy works fine (first samples of previous block are copied to last two samples of the “history” buffer), but once the new block data is copied into that buffer, the end values [up to N-1] are not coherent with the stored values [N,N+1], which of course causes very noticeable clicks. I’ve checked this by using breakpoints in the code and looking into a float array that I copy the buffer content into (super dirty way of doing things, but hey!).
At this point I’m thinking either:
1/ I missed something so stupid that I can’t find it in my code, no matter how much I look.
2/ I am missing something fundamental about how AudioSampleBuffer and processBlock work and I’m going about this the wrong way.
Any help would be greatly appreciated!
This is my first post on the forum so I hope the format is OK, I will post part of my code below:
void prepareToPlay (double /*sampleRate*/, int /*maxBlockSize*/) override {
int bSize = this->getBlockSize();
int numChannels = this->getMainBusNumInputChannels();
// 2nd order filter so we need a history going back by 2.
// Only set the size if it is not correct (i.e. it hasn't been initialised yet)
// The x_hist and y_list are allocated empty inside the constructor
if (x_hist->getNumSamples() != bSize + 2) {
x_hist->setSize(numChannels, bSize + 2, true);
x_hist->clear();
}
if (y_hist->getNumSamples() != bSize + 2) {
y_hist->setSize(numChannels, bSize + 2, true);
y_hist->clear();
}
}
Process Block code:
void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) override
{
AudioSampleBuffer mainInputOutput = busArrangement.getBusBuffer (buffer, true, 0);
float a0 = *p_a0;
float a1 = *p_a1;
float a2 = *p_a2;
float b1 = *p_b1;
float b2 = *p_b2;
int N = buffer.getNumSamples();
// Write the first two samples of the old values to the end of the history buffers
for (int j = 0; j < 2; j++) {
for (int i = 0; i < mainInputOutput.getNumChannels(); i++) {
*x_hist->getWritePointer(i, N + j) = *x_hist->getReadPointer(i, j);
*y_hist->getWritePointer(i, N + j) = *y_hist->getReadPointer(i, j);
}
}
for (int j = 0; j < N; j++)
for (int i = 0; i < mainInputOutput.getNumChannels(); i++) {
// Copy the N samples of the input buffer into x_hist (makes things clearer)
*x_hist->getWritePointer(i, j) = *mainInputOutput.getReadPointer(i, j);
}
// For each sample in the buffer (counting backwards so we can calculate the Y(n) series correctly!)
for (int j = N - 1; j >= 0; j--)
{
for (int i = 0; i < mainInputOutput.getNumChannels(); i++) {
// Calculate the Y(n) series, going up backwards from the older values
*y_hist->getWritePointer(i, j) =
a0 * (*x_hist->getReadPointer(i, j))
+ a1 * (*x_hist->getReadPointer(i, j + 1))
+ a2 * (*x_hist->getReadPointer(i, j + 2))
- b1 * (*y_hist->getReadPointer(i, j + 1))
- b2 * (*y_hist->getReadPointer(i, j + 2));
}
}
// Final copy to the output buffer
for (int j = 0; j < N; j++)
for (int i = 0; i < mainInputOutput.getNumChannels(); i++) {
mainInputOutput.setSample(i, j, *y_hist->getReadPointer(i, j));
}
}
I’m on Windows, building with VS2015, and debugging with the Plugin_Host example code.