[SOLVED] OwnedArray of custom class as member variable of PluginProcessor

Hi !

Cpp newbie here, so sorry if this is stupid, but after googling and experimenting, I can’t make any sense of this.

I’m trying to use an array of (custom_class) filters as a member class of my PluginProcessor, to have one filter per audio channel.

When I do the following with a stereo channel, everything works fine:

class BarebonefilterAudioProcessor  : public AudioProcessor
{
...
private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BarebonefilterAudioProcessor)
    //==============================================================================
    MyFilter filterL;
    MyFilter filterR;
...

void BarebonefilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();
    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

        float* channelDataL = buffer.getWritePointer(0);
        float* channelDataR = buffer.getWritePointer(1);
        filterL.filterNSamples(channelDataL, buffer.getNumSamples());
        filterR.filterNSamples(channelDataR, buffer.getNumSamples());
}

However, when I try to generalise the principle (I guess very naively), I do this, and the plugin outputs a full DC offset, it seems. Well, something with the way I handle the array is messed up.

class BarebonefilterAudioProcessor  : public AudioProcessor
{
private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BarebonefilterAudioProcessor)
    //==============================================================================

    OwnedArray<MyFilter> filters;

...
BarebonefilterAudioProcessor::BarebonefilterAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       )
#endif
{
    filters.clear();
    for (int i = 0; i < getTotalNumInputChannels(); ++i)
    {
        MyFilter f;
        filters.add(&f);
    }
}
void BarebonefilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();
    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

        for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
	float* channelData = buffer.getWritePointer(channel);
	filters[channel]->filterNSamples(channelData, buffer.getNumSamples());
    }
}

Are the instances of MyFilter going out of scope or something ? I can’t tell what the issue is.

Thank you for your time

Yes, that is exactly the point. MyFilter f; creates an instance on the stack, that is freed when the round brackets end.
versus
f = new MyFilter();
is created on the heap and will exist until somebody deletes it. That’s why you put it into the OwnedArray, and it will be deleted when no longer needed.

Either do

filters.add (new MyFilter());

Or if you need to do something with the thing before adding:

auto f = new MyFilter();
filters.add (f);

Another caveat, you seem to do only stereo, so you will probably be fine.
But you should do the creation of filter instances in prepareToPlay, as the number of channels can change after the constructor (when the Processor and the host negotiate the actual bus configuration to be used)

HTH

1 Like

Thanks!

I got confused as to how memory was handled in C++ (my Java was showing…).
Thank you very much for the help, it all works well with new MyFilter() inside prepareToPlay()

Cheers

Actually, OwnedArray’s add returns the newly added item so you can do this in one tidy line:

auto f = filters.add (new MyFilter());
2 Likes

Top tip @jrlanglois! Hadn’t realised that before.