Potential aliasing issues with AudioSampleBuffer


#1

“Violating Type Rules: It is undefined behavior to cast an int* to a float* and dereference it (accessing the “int” as if it were a “float”).”

The article goes on to point out that code that does this - like juce::AudioSampleBuffer does seem to - will occasionally have failures due to undefined behavior in an optimized build…

Near as I can tell, in the specific case of juce::AudioSampleBuffer, any conceivable error would result in changes in one AudioSampleBuffer not being reflected in another thread or processor for a short time - only if the buffer were a fixed point buffer. So while I can’t tell whether or not this problem really does occur, it’s quite conceivable that this problem has been intermittently occurring in some people’s programs that use fixed point buffers, and simply giving tiny intermittent glitches…

Lucky for you :smiley: there’s a fix that guarantees a good result and doesn’t break client code… simply create the following union:union Sample { float floatSample; int intSample; short cd_sample[2]; // So Tom can pack data from his CDs and not waste half his memory. } and then put that into your channels arrays.


#2

Why not just make a copy of the AudioSampleBuffer code, rename it, and turn it into a class template with the type of sample as a template argument?

template <typename SampleType>
class SampleBuffer
{
public:
  //...
  SampleType* const* getArrayOfChannels () const;
  //...
};

This way you get the full benefit of type safety and all that jazz…and no need for an unsightly C-style union.


#3

That won’t fix the issue in the underlying Juce code!

Basically, if you reinterpret cast from one pointer type to another in your C++ code, you really have two choices - either turn off the correct optimizations in your compiler, or resign yourself to the possibility of having intermittent optimization bugs… using a union alerts the compiler to the fact that you’re doing that with this memory so it turns off the optimization just for that specific data structure.


#4

I think you read more into it than actually is there. The piece you quote, I believe was referring to the result of mixed dereferencing of those pointers. If you address memory using a float pointer but then cast it to an int pointer and start using it, that’s fine.

But if you take an int pointer, store an int value there, then cast the pointer to a float pointer, and read a float value from there, that result is undefined.

In your case you are treating it as int / short consistently so there should be no problem.

I don’t see what the problem is with the underlying Juce code. Has anyone encountered a bug? I think not.


#5

In DSP code, it is often the case that subtle errors will result not in obvious reproducible failures but subtle degradations in the result.

If occasionally you got a wrong sample from a previous iteration in your buffer, how noticeable would it really be as having this cause?

Now, I think your main point that you don’t ever look at the same block of memory as two different types, but I’m still really not sure that I could really prove that all these reinterpret_casts back and forth in that class really never causing aliasing issues during the same block of code execution.

I’ll bet it’s probably fine.


#6

But then again with the template this is a moot point - what’s wrong with the template approach again? Why must we use a union?