std::clamp works fine with negative numbers, but the lo parameter must be less than hi. Isn’t the actual bug the fact that numAvailableSamples is negative? The question is, why is startSampleInFile greater than the number of samples? Or is it an overflow issue with the cast to int?
Not numAvailableSamples but the number of samples requested (numSamples) when calling the read function.
We can guess, but I think the debugger is the better tool.
Could be trying to draw a fragment of the thumbnail with a higher startTimeSeconds than endTimeSeconds when calling drawChannel().
It could also be an inconsistency in the audio file, where totalNumSamples is different from the actual number of samples, although I don’t know if that would be the place to break.
I checked up the call stack and all the values look legit until the call to readSamples, where numSamples becomes negative.
It looks like this happens in the following function which is called at the start of readSamples:
/** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie
beyond the end of their available length.
*/
static void clearSamplesBeyondAvailableLength (int* const* destChannels, int numDestChannels,
int startOffsetInDestBuffer, int64 startSampleInFile,
int& numSamples, int64 fileLengthInSamples)
{
if (destChannels == nullptr)
{
jassertfalse;
return;
}
const int64 samplesAvailable = fileLengthInSamples - startSampleInFile;
if (samplesAvailable < numSamples)
{
for (int i = numDestChannels; --i >= 0;)
if (destChannels[i] != nullptr)
zeromem (destChannels[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
numSamples = (int) samplesAvailable;
}
}
I agree @Daniel but I guess there is a reason for that clamp that is there already. For now I’m adding it here just to patch this issue. Not ideal but works for now.
I’m not a fan of assertions only in these cases, as you might have an edge case that only appears on a special file on a user session in a live situation and there is nothing that guards against that in release.
I really would be interested if the call from outside the juce code is legit, because there is a chance of a bug, in which case it would be nice to find a proper fix.
There’s definitely something going on, as there are two calls to draw the thumbnail in quick succession, and it’s the second time that the negative value appears.
Adding a lock or atomic guard did not fix the issue. Definitely needs resolving properly.
I think I’ve seen this in other AudioFormatReader types, I thought I got them all a little while ago, but evidently not, maybe it didn’t make it through review I’ll go back and check. If memory serves me correctly after clearSamplesBeyondAvailableLength I think it should have…
if (numSamples <= 0)
return true;
I can’t remember the details but my conclusion was that as a precaution that should just always be the first thing to do.
I see a bit of int mixed with int64. All sample-length related values should be kept in int64 to allow for larger and larger files. 10+ years ago, a 4 GB audio file was unthinkable, not so today. Storage is cheap, and people sometimes do crazy things.
I think it is consistent as in, if it refers to a full file it uses int64, but for AudioBuffer offsets it uses int.
It feels the right thing to do, although you are right, there are situations where you have to double check type propagation. And following the trend, I guess we will see more warnings coming in that realm.