2D Array is not filled

Why is my 2D array notesAndSamples not populating? It always remains empty in both dimensions.

File::getCurrentWorkingDirectory().getChildFile(samplePath) gives the correct path and the files are loaded, but the AudioTransportSource* sample is not added to the 2D array. Though other one-dimensional arrays readers and readerSources are filled normally.

What’s wrong?

Announcement:

    Array<AudioFormatReader*> readers;
    Array<AudioFormatReaderSource*> readerSources;
    Array<Array<AudioTransportSource*>> notesAndSamples;

Method:

void NewProjectAudioProcessor::LoadSample(int noteNum, String samplePath)
{
    AudioFormatReader* reader = formatManager.createReaderFor(File::getCurrentWorkingDirectory().getChildFile(samplePath));
    readers.add(reader);

    AudioFormatReaderSource* readerSource = new AudioFormatReaderSource(reader, true);
    readerSources.add(readerSource);

    AudioTransportSource* sample = new AudioTransportSource();
    sample->prepareToPlay(prepareSamplesPerBlock, prepareSampleRate);
    sample->addChangeListener(this);
    sample->setSource(readerSource, 0, nullptr, reader->sampleRate);

    notesAndSamples[noteNum].add(sample); //THIS NOT ADDED
}

What’s the size of notesAndSamples when you call notesAndSamples[noteNum].add(sample);?

Looks like it could be empty, in which case you need to call something like notesAndSamples.resize (numNotes) before calling that.


Also, have a look at juce::OwnedArray which is designed for holding heap-allocated objects like you’re doing here. juce::Array won’t properly deallocate the memory of the pointer’s it’s holding so unless you manually go through and delete them all before the array is destroyed.

I thought it should expand on its own. Ok, I set the initialization for the array:

    notesAndSamples.resize(999);

    for (int i = 0; i < 999; i++)
    {
        notesAndSamples[i].resize(999);
    }

The first layer of the array is incremented:

DBG(notesAndSamples.size()); //result 999

But the second layer does not resize and the elements are still 0:

DBG(notesAndSamples[0].size()); //result 0

juce::Array::operator[] returns a copy of the element at the given index, if the index is beyond the number of elements in the array, it’ll return a default value (without adding it to the array). So even if the index is correct, when you then call add() on the returned array, you’re adding to a temporary copy of the array, not the actual array being held by notesAndSamples.

You’d want to use getReference (noteNum) to get a reference to the element at that index, which you can then modify.

1 Like

You can avoid the problem of working on a copy by using a ranged for loop:

notesAndSamples.resize(999);
for (auto& noteAndSample : notesAndSamples)
    noteAndSample.resize (999);

The main benefit is that there isn’t the danger of messing up the size of the array and the end condition of the loop. And using the & reference in the auto makes sure not to use a copy.

The other issue is the use of raw pointers, which is very prone to create memory leaks. Something to be cautious about especially in a RAM hungry instrument like you are planning.

There is no need to keep the pointer to the readers, because you will always access it through the AudioFormatReaderSource and it is owned by the source thanks to the bool set to true.

FTR: AudioTransportSource is probably a bad choice for an instrument because of it’s asynchronous nature, as I mentioned on the discord already.

2 Likes

I am very grateful to everyone who answered. Now it works!

 notesAndSamples.getReference(noteNum).add(sample);

What you wrote is very interesting, but so difficult! I used to work with C# and everything is much simpler there. Everything new that I meet in C++ after C# causes rejection in me.

I feel your pain. For me it was the opposite effect. I had a brief encounter with C# in Unity, and I totally failed to see how I could access things. They seemed always to “be just there”, unless when they were not. I gave up.

For me it is natural to have the life time of things explicit. I think that is the hardest part to swallow.
But we should be happy with modern RAII methods, so we don’t need to keep manually track of lifetimes, making sure having new and delete in pairs… looking back it is amazing that old stuff worked at all…

2 Likes