iPlug2 Forum

How do I fix this convolution algorithm?

What can I do to fix this algorithm?

My goal is to create a basic convolution plugin, using my own algorithm. Right now it outputs very loud and high-pitched frequencies and doesn’t do its job at all. I can’t seem to find what’s wrong with it.

My goal is to take in 100 samples at a time (and while that’s happening the output is just 0), process them all at once in a loop, then output them one by one while taking in new inputs to process next.

Note: I just started learning about DSP and I’ve barely even started to work on this algorithm. I’m not at all sure how the ProcessBlock function works, but I’m only processing one sample at a time. Not sure if newIns and newOuts should be of type sample or double, but from what I’ve tried either type works.

I’d also be more than happy to receive additional tips because I’m very new to C++.

sample newIns[100]; // Input array
int newInsCount = 0;
double ir[4] = { 0.8, -0.3, -0.2, -0.1 }; // The impulse response
sample newOuts[100 + 4 - 1] = {0}; // Output array
int newOutsCount = 0;
bool outsDone = false;

#if IPLUG_DSP
void MyNewPlugin::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
  const double gain = DBToAmp(GetParam(kGain)->Value());
  const double clip = DBToAmp(GetParam(kClip)->Value());
  const int nChans = NOutChansConnected();

  for (int s = 0; s < nFrames; s++) {
    for (int c = 0; c < nChans; c++) {

      // Basic clipping process
      sample input = inputs[c][s];
      sample processed;
      if (input*gain >= 0) processed = fmin(input*gain, clip);
      else processed = fmax(input*gain, -clip);

      // Inserts a sample into the inputs array
      bool isTaken = false;
      for (int i = 0; i < 100; i++) {
        if (i == newInsCount) {
          newIns[i] = processed;
          isTaken = true;
          newInsCount++;
          break;
        }
      }

      if (isTaken == true) outputs[c][s] = 0;
      else {
        if (outsDone != true) { // Convolving the inputs with the impulse response
          for (int i = 0; i < 100; i++) {
            for (int r = 0; r < 4; r++) newOuts[i + r] += newIns[i] * ir[r];
          }
          outsDone = true;
          newInsCount = 0;
        }
        
        for (int i = 0; i < 103; i++) { // Loading a sample from the output array into the actual audio output
          if (i == newOutsCount) {
            outputs[c][s] = newOuts[i];
            newOutsCount++;
            break;
          }
        }

        if (newOutsCount == 103) {
          outsDone = false;
          newOutsCount = 0;
        }
        
      }
    }
  }
}
#endif

The ProcessBlock function gives you a number of samples for you to process at a time, which is the “buffer size” that you setup in your DAW. So don’t need to process your samples in chunks of 100; that sounds a bit redundant with the block you get in this function. What you do is you read the inputs array, process it, and write the result in the outputs array.

If your goal is to perform a manual convolution you only need a buffer the size of your IR minus 1 (in this case 3) in order to use the value of the last 3 samples from one block at the beginning of the following block.

And don’t forget to perform the convolution in reverse order. That is:

outputs[c][n] = inputs[c][n]*IR[0] + inputs[c][n-1]*IR[1] + inputs[n-2]*IR[2] + inputs[n-3]*IR[3]

You can see that when n = 0,1, and 2, you will need to resort to the values of the signal stored in your array, and probably, you will have to code these cases apart from the rest. And when you want to use IRs of larger sizes you may want to generalise this whole procedure, but bear in mind that if the IR is too large then manual convolution will be too slow, and you will do better resorting to FFT. Also, if your plugin works in stereo then you will need 2 buffers, one for each channel, which is not a big deal at sizes this small.

I prefer to use float type for the samples… I never knew there was a sample type, but I suppose if it works then you could use that if you prefer.

Thank you so much!

I’m definitely planning on using FFT in other projects, I just really wanted to try manual convolution first to understand the concept more.
I really don’t know what the sample type does, I just saw it being used in the ProcessBlock arguments.

Another very basic C++ question, is there an easy way to get the length of an array? From what I’ve read, there isn’t a built-in function that returns the length.

l think you can use

int ir_length = sizeof(ir)/sizeof(ir[0]);

to get the array’s length.

A better option is to use the class std::vector which is a more flexible type of array, with built in functions for size, push and pull elements, &c.

And you’re right about the sample type, for some reason I had never noticed it before :joy: but it seems it’s only an alias for either float or double according to some inner macros