I'm having problems with OwnedArrays (or pointers in general)

Hello, I’m new at programming in C++ and juce is making me really uncomfortable, I’m still learning.
Im trying to program a Mid-Side EQ with 5 bands and since I know that I can’t use the same filter for all the blocks I used an Owned array for everything.
Excuse me in advance, the code maybe is not the most elegant but I’m trying to make everything more clear because I really can’t figure out where is the problem.

I have this in the private part of the filter module (the buffer is already split up in M/S:

juce::OwnedArray<juce::dsp::LinkwitzRileyFilter<float>> filtersMid, filtersSide;
juce::OwnedArray<juce::AudioBuffer<float>> buffersMid, buffersSide;
juce::OwnedArray<juce::dsp::AudioBlock<float>> blocksMid, blocksSide;
juce::OwnedArray<juce::dsp::ProcessContextReplacing<float>> contextsMid, contextsSide;

I will try to not post all the code (since is just a repetition of the following lines in loops). These are some of the lines I used.


filtersMid.add(new juce::dsp::LinkwitzRileyFilter<float>);


juce::dsp::ProcessSpec spec;
spec.maximumBlockSize = samplesPerBlock;
spec.numChannels = 1;
spec.sampleRate = sampleRate;

buffersMid.add(new juce::AudioBuffer<float>);
buffersMid.getUnchecked(0)->setSize(1, samplesPerBlock);
blocksMid.add(new juce::dsp::AudioBlock<float>(*buffersMid.getUnchecked(0)));
contextsMid.add(new juce::dsp::ProcessContextReplacing<float>(*blocksMid.getUnchecked(0)));

And these are some of the lines I used in processBlock:


The last line is giving me Access Violation Exception, meaning something is wrong with pointers, but I really don’t know how to solve it. Only vst crashes, while standalone starts, but not working as intended.
Thanks in advance for the help!

First of all a word regarding general debugging strategies: When you run your plugin/executable under the debugger and see that there is an access violation in a certain line of code you can inspect all variable values including member variables of all class instances at that state so that you should get a very strong hint where excactly the invalid pointer is located. If the variable that triggers the problem has been created in your local scope, you can set a breakpoint to where it is created and step through the code to spot the place where things go wrong. If the invalid value has been passed to you from an upper function call, you can step up stack frames and inspect values there.

Then, your implementation strategy looks a bit strange to me and has a lot of OwnedArray usage that seems a bit unecessary to me. If I got you right you want to build a 5 Band parametric EQ with individual bands for mid and side, did I get that right? In this context I don’t quite get why you are using the LinkwitzRileyFilter which is a cut filter type mainly for crossovers. But before I start proposing a different strategy, would you mind clarifying how you want your EQ to work in detail?

Exactly. I read that Linkwitz-Riley filters are perfect for crossovers since I have to make 5 bands for every channel and then sum them up (for now the gain part is not implemented, i need to solve this exception first).
Basically this is the process I’m following for 3 bands: I copy the buffer in 2 different buffers, one is lowpassed at cutoff frequency q, one is highpassed at cutoff frequency y, the remaining band is obtained by taking the original buffer and subtracting the filtered ones. For 5 bands I repeat this process 1 more time and then this has to be applied to the other channel.

Another strategy would be having 2 copies of the original buffer, use a lowpass at the cutoff frequency q for the first, use an highpass with the same cutoff frequency q and then make a copy of the second copy-buffer to reapply low pass and highpass etc… until I have 5 bands per channel.

I will try to apply this strategy from now on, but still I don’t understand if I used OwnedArrays correctly, even if they are not elegant in the way i created them and maybe are too many (i can optimize the code later).

Thanks for the help by the way.

UPDATE: I’m inspecting the variables like you said and there is really something going on when using ProcessContextReplacing, since at runtime the outputBlock and the inputBlock pointers are totally wrong. Maybe i instantiated them in a bad way?

I would advise against using juce::dsp::AudioBlock or juce::dsp::ContextReplacing on the heap and not using them as members. They are just wrappers to hand the sample data in an easy digestable format.

You create the AudioBlock from using the constructor with the AudioBuffer it shall refer to.
Same with the context. This is because the AudioBuffer might not be the same on each processBlock() call, and you must not access that location outside the processBlock() for thread safety reasons.

It should look as simple as that in your processBlock():

void MyAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
    juce::dsp::AudioBlock<float>              ioBuffer (buffer);
    juce::dsp::ProcessContextReplacing<float> context  (ioBuffer);
    filter.process (context);

Hope that helps

So basically every AudioBlock and Context has to be created in the processBlock since they are “lightweight” and should not cause any problem even if created in the heap, right?

Now imagine I have to process more copies of the buffer in the same processBlock: should i create an AudioBlock and a Context for every copiedBuffer? Like this?

juce::dsp::AudioBlock<float>              ioBuffer(*buffersMid.getUnchecked(0));
juce::dsp::ProcessContextReplacing<float> context(ioBuffer);
juce::dsp::AudioBlock<float>              ioBuffer(*buffersMid.getUnchecked(4));
juce::dsp::ProcessContextReplacing<float> context(ioBuffer);

And isn’t the process quite taxy on the CPU since I’m allocating in the audio thread?

Many thanks for the help again!

Every object on the heap creates problems. That’s why they are created on the stack (without calling “new”)

The AudioBlock just references the underlying data. Creating multiple AudioBlocks wouldn’t make a difference.

Instead you use ProcessContextNonReplacing which takes two AudioBlocks, one in and one out.
Because you cannot create a sample buffer in processBlock in a safe manner, you need to prepare as many AudioBuffers as you will need as members and setSize() them in the prepareToPlay()

void MyAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
    juce::dsp::AudioBlock<float>                 inputBlock (buffer);
    juce::dsp::AudioBlock<float>                 lowBlock (lowBuffer);
    juce::dsp::ProcessContextNonReplacing<float> contextLow  (inputBuffer, lowBuffer);
    filter.process (contextLow);
    juce::dsp::AudioBlock<float>                 midBlock (midBuffer);
    juce::dsp::ProcessContextNonReplacing<float> contextMid  (inputBuffer, midBuffer);
    filter.process (contextMid);

Perfectly explained, it’s working wonders now. Thank you guys!