I think I found a bug in JUCE:
I tried using juce::Array<juce::AudioBuffer to hold several audio buffers.
I added them into the Array using Array::add().
I referenced a buffer from the array (the last one in my case). Then I took its getWritePointer() and wrote a number into the audio data.
Then I took getReadPointer() and scanned through the whole list.
Then I checked specifically if the value I wrote there originally exists. It did not. I got 0.0f instead of the number I had written there.
The value is there if I check it’s existence right after I have written it there. If I read through the buffer in any way, the value disappears, which probably means that the value was read from CPU cache instead of memory. Which probably implies that the audio buffers memory is not initialized and relies in some unmananged area.
The bug goes away if I don’t use juce::Array, but change it to juce::OwnedArray.
How are you doing that?
juce::Array::getLast() returns a copy of the last element, not a reference or a pointer. So if you’ve used that, or any of the other accessors that return copies (e.g.
operator), then any changes you make aren’t going to persist.
I’m pretty sure that you are not experiencing a bug, but – as @ImJimmi already pointed out – completely documented but still unintuitive behaviour of the
juce::Array class. It just eturn copies where you would intuitively expect them to return references in a lot of places.
This is one of the main reasons that we switched to
std::vector as our go-to container quite a while ago, even if the
juce::Array has some nice implementation details under the hood. And I’ve got the impression that even the juce team uses
std::vector more and more in their own implementations.
It should probably become very obvious if you assigned the value to an intermediate reference in your code like e.g.
auto& buffer = myAudioBuffers;
jassert (buffer.getNumberOfChannels() > 1);
This assignment should not even compile as you are assigning a temporary object to a reference.
Given that this Array::operator killed so many developer hours, I vote strongly to deprecate it and remove it. Changing it to the correct behaviour would lead to tons of silent bugs.
Either you torture existing users or coming users.
To add some commedy to the drama, there is this:
ElementType operator (int index) const;
ElementType & operator (const int index);
And for more entertainment, Array is not inheriting ArrayBase, but is rather composed of an ArrayBase.
I think it is great not to inherit ArrayBase, but calling it Base and not inheriting of it is genius
(I wouldn’t mind to replace juce::Array with std::vector, but I know there is a special relationship in the JUCE team towards juce::Array…)
You guys seem to be right. What I was doing was this with the Array of AudioBuffers:
There’s the operator .
Now that I think of it, it remember running into this same problem before. Somehow it’s so unintuitive detail of the Array that I always forget that I shouldn’t do that.
I highly vote for making that feature deprecated and wiping it under he carpet.
If we’re going to deprecate anything, I’d say to deprecate the whole
juce::Array class and start from scratch.
Call the new one
juce::DynamicArray to avoid confusion with
std::array which works very differently, and make sure all the accessors return references by default.
Something that would really make JUCE’s array class be a good replacement for
std::vector would be to add methods like
Shameless self-promotion: If you are interested in something like that you might want to have a look at the project which I presented at ADC: GitHub - sonible/VCTR: A powerful C++ 20 wrapper around your favorite standard library containers
It’s still in an early beta phase and not feature complete yet but will offer the exact features you are mentioning plus a ton of cool math stuff for numerical vectors once it reached version 1.0 – probably in the beginning of 2023