Int vs int64 in Juce audio


#1

Hello! I’m back after a rather long holiday… where I actually saw met Juce(*), er, Jules in the flesh, as well as his charming gf.

While I was vacationing, I turned on all the (useful) compiler warnings from my codebase and removed them.

In practice, this turned out to be various arithmetic warnings - signed/unsigned comparisons and narrowing conversions. Quite instructive and found at least one latent bug and a lot of potential bugs have been noted for “very large sample files” (with > 2 gig file sizes).

However, there seems to be an inconsistency between the usage of int64 aka long long int and int for sample counts.

In some places, like AudioSourceChannelInfo, Juce uses ints; in other places, like PositionableAudioSource, Juce uses int64s; and as a result, I have downcasts in various places.

Now, I can see the reasoning in those two - you might be thinking that PositionableAudioSources might be very large and need 64 bits, which is certainly true, but that AudioSourceChannelInfo represents “small” areas of memory and we’d never have 2 gigs worth of samples in memory - but that isn’t necessarily true in a 64-bit world, there might be large offsets at the very least.

I just have a single type for all sample counts and offsets - it’s called Samples<44100> and you can see the comment here where I had to add another method when I turned on strict arithmetic warnings. (You can see its sibling RealTime here…)

Certainly not an “action item” but just a data point for future changes.

(* - I really did type Juce initially)


#2

Delightful to have met you too, Tom!

Yep, I did generally choose 64/32 sized values based roughly on whether it was a file or an in-memory buffer, assuming that even in a 64-bit system people were unlikely to have a 2GB in-memory buffer. Of course what I should have used in the first place would have been a ssize_t instead of an int, which would have solved the problem neatly. It’s one of those things that I’ll eventually sort out when I’m having an unproductive day and idly trawling through the codebase tidying-up minor stuff.


#3

ssize_t, hmm!

Interesting, I didn’t know about it - for the benefit of other readers, it’s like size_t, inasmuch as it’s an int type that represents “offsets in a buffer” and as such is 32-bit or 64-bit according to how you build your program - except that it’s signed.

I think that’s probably better than size_t for almost all purposes. 32-bit machines generally only really handle 31-bit addressing so the largest buffer you can practically have is 2 gig anyway. It’s extremely easy to make subtle mistakes with unsigned numbers and comparisons or arithmetic that are only revealed when you get into edge cases.

So what I really need are classes to represent “time” - RealTime, MemorySamples and DiskSamples. RealTime is floating point time in seconds, DiskSamples is always an int64, and MemorySamples is an int32 or int64 depending on how you’re building. (Not that I’m going to do this now but it’s good to know what the “correct” answer is.)

I should again throw in a good word for using classes to deal with your times - I did this basically to prevent “accidental” casts between time in seconds and time in samples, after the second time I made that mistake, and it’s worked extremely well for me. Samples.h is a good example, you can see how you can construct one from any integer type but from no floating point type.

And of course it costs nothing once the optimizer is done because it’s just treated like a regular int for computation purposes.

But again, I like fancy. :smiley: Your Mileage May Vary.