Custom Sample-Based Processing Question

Greetings! I’ve developed a few signal processing algorithms using JACK/C in Linux (Fedora) and I’m trying to port them to JUCE. Most of these use sample by sample processing and fixed float (or double) arrays as buffers. I need to do it like this because I would like to implement stuff like UPOLS and NUPOLS convolution algorithms and some specific reverb topologies. The questions I have are:

  1. Could I declare the buffers as pointers in the header file and define them in prepareToPlay()? For example: Say I want to write a simple delay, I know that I need a buffer for each input channel. So I write this to the header file: float **delay_buffer;

And then, in prepareToPlay() I write:

delay_buffer  = new float*[num_channels];
for (int i = 0; i < num_channels; i++){
		delay_buffer[i]  = new float[samples];
	}

If the buffer size changes, will prepareToPlay() be called again? Do I need to call something like if(delay_buffer != NULL) free(delay_buffer) to make sure the new buffer gets created properly?

  1. I’m aware that JUCE has a great dsp module that makes life easier when you’re trying to chain filters or other basic effects, and I’m thankful that I don’t have to implement these things from scratch. I would, however, like to know how can I extract individual frame arrays for each channel. In JACK I could just get (in the simplest case) one in array and one out array both with n samples and then, for example, define a simple gain process as:
for (int channel = 0; channel < num_channels; channels++){
  for (int frame = 0; frame < buffer_size; frame++){
    out[channel][frame] = 20*log10(gain) * in[channel][frame];
  }
}

This is, obviously, a trivial example; but what if I want to apply a stereo mixing matrix to the channels? I would have to write something like:

for (int frame = 0; frame < buffer_size; frame++){
  x1 = in[0][frame];
  x2 = in[1][frame];
  out[0][frame] =  0.707*x1 + 0.707*x2;
  out[1][frame] = -0.707*x1 + 0.707*x2;
}

How could I do such a thing in JUCE? I’m new to this framework, so I would greatly appreciate any help learning the ropes!
(Also, kindly refrain from commenting things such as ‘why would you want to do that?’)

For the buffer allocations : don’t use something like new float[samples] in modern C++ code. In Juce you can use the AudioBuffer class for audio buffers, that internally takes care of allocating and deallocating the memory. You can resize the number of channels and samples in that simply with the setSize call. Of course that should generally be done only in the prepareToPlay method, as you should not make dynamic memory allocations in the audio thread code.

If you for some reason don’t want to use Juce’s AudioBuffer, std::vector<float> or std::vector<std::vector<float>> are other options for handling dynamic buffers.

Your matrix mixing example can be written in Juce basically the same way you already did it, unless you really need the separate input and output buffers :

void myplugin::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midi)
{
  float** insouts = buffer.getArrayOfWritePointers();
  for (int frame = 0; frame < buffer.getNumSamples(); frame++){
    float x1 = insouts[0][frame];
    float x2 = insouts[1][frame];
    insouts[0][frame] =  0.707*x1 + 0.707*x2;
    insouts[1][frame] = -0.707*x1 + 0.707*x2;
  }
}
2 Likes

Brilliant, thank you very much for the reply!