How do I set audio output bit depth, or why can't I?

Years ago someone asked the same question. The answer at the time was that JUCE autoselects the best format for the relevant audio device. There’s still no setBitDepth.

I know my external usb dac/amp works with 24 bit data at best, but if I recall from some ALSA experimentation it ‘accepts’ 32bit floats, which means the truncating would happen somehow (I’d rather do it myself) or there’d be unnecessary downconversion. What does JUCE do here and what should I?

TBH you can just ignore the bit depth. Only a tiny number of audio APIs actually let you change it, and even fewer devices support different depths. The JUCE classes will always use the highest bit depth if possible, and since you send the data as floats anyway it shouldn’t matter. (And nobody can actually tell the difference, of course…)

1 Like

I can understand the usefulness of floats for processing, but how are they truncated and how do I control dithering?

This stuff very rarely matters. What’s you actual reason for wanting to do this?

I want a synthesis/composition program for me. Dithering most certainly matters for writing audio files, but then bit perfect music players need to know the bit depth of the sound card too, don’t they?

Again, how are floats truncated?

Thanks for trying to help.

No, why would they?

The conversion either happens in the audio driver, and almost all drivers will probably just truncate to 16/24 bits. In juce code where we have to do this, we also just truncate it, because no human can tell the difference.

If you want to add extra dithering, you can do that yourself before writing the buffers, but honestly, it’s a waste of effort.

The only processing an audio driver should be doing in a pro audio setting is either mixing (conversion is necessary if the inputs are different bit rates or depths) or uncompressing a lossless audio stream. What does bit perfect mean to you?

How are floats truncated? Do I need to care about endianness? From a performance perspective now, knowing which bits are unused is great for minimizing branching in loops (phasers, for example), and critical for getting fixed point or integer-like processing right (correctness perspective).

Seriously, none of the things you’re asking about matter at all.

Just focus on writing good pure-floating-point algorithms and don’t waste your time thinking about implementation details below the driver level.

So is your answer that floats are 32 bit IEEE-754 and the lowest 8 or 16 logical bits in the significand are truncated? Is this how JUCE handles it?

EDIT: Sorry, not 752. Significand, not mantissa.

I will add that Csound doesn’t emphasize the dithering strategy either.

Nope. My answer is that I couldn’t give a rat’s arse about how floats get truncated. And I’ve never heard of an expert in the field ever suggesting it was of the slightest importance.

In the places where JUCE does it, we use plain old C++ float-to-int conversion, which is probably best defined as “whatever your compiler and CPU happen to do”.

I actually think you can hear the difference. Someone much smarter than me could probably give a better reason on why or why this is or is not…

Upon implementing a delay line with all floats versus doubles I noticed a difference. This was when I first started years ago… which led me to my rule… if audio goes through a variable it must be a double, user params can be floats.

However, here is my attempt at a defense. The “noise floor” or that white noise at negative whatever db is technically not in the recording. I assume with perfect interpolation there would be zero or no noise.

Which leads me to my point that it is the error in playback causing a higher noise floor and thus lower quality. I found this thread wondering what would be a good decimal size to implement with custom variable types to replace my “dull” floats? 8,16,50,100!!!

I leave you with a play on words why “truncating” is a problem which is the same problem with words…

I asked a sample, how it was doing it said “bad”

I asked a sample, how it was doing it said “not good”

I asked a sample, how it was doing it said “ok”

I asked a sample, how it was doing it said “not bad”

I asked a sample, how it was doing it said “good”

Every statement “means” the same as the previous “statement” but somehow it doesn’t? Nah mean? Idk just programmed 18 hours straight. Night fellas.

Also this too,

Encase anyone else didn’t know what in the hell IEEE-754 is like me. I also learned what truncating is today. I always called it chopping off. lol

Ok, for real my day is truncating goodnight :joy:

IMHO there was no need to necro this thread. All we need to know is here:

You can design your code to use double processing, that is no problem.

I am torn if there is a need to have the AudioIODeviceCallback to accept doubles in a special mode similar to the AudioProcessor::isDoubleProcessingSupported() flag.

Adding dither noise is probably best added in your floating point domain anyway, i.e. float or double as you like it.

If you want to write to audio files, it will be discretised into the target format by the AudioFormatWriter depending on how you set that up. Truncating will then happen in the discrete domain quite naturally.

1 Like

perhaps there is an argument that the forum could be set up to lock threads that are older than some arbitrary cut off (6-12 months?). If someone wants to continue the discussion they can start a new thread and reference the old one with a link. eg.

I found [this thread](https://forum.juce.com/t/how-do-i-set-audio-output-bit-depth-or-why-cant-i/17543)
but I have further questions...

I’m pretty sure the old thread will forward reference to the new one at the bottom of it, and we then avoid necro-ing potentially useless/defunct replies in the original, and will go a long way to stopping potential noise replies on long dead threads.

Something for the JUCE team to consider…

4 Likes

Already discussed in the thread below ; you should probably necro it! :rofl:

1 Like

haha, yeah I won’t do that. Some good arguments against there, maybe 6-12 months is too hasty, but still think there is an argument that beyond 24 months there is very likely no good justification for reviving a thread. But, that’s like, just my opinion, man… :smiley:

1 Like

woke up and found this

I’ve been trying to do the math, of the exact decimals needed for losses processing. However, I did find initializing a long double to 0.45 might render 0.45000000000123469234. -[Note] boost has a decimal variable class with as many variables as you would like…

I think at this point in time … a double buffer is absolutely necessary and I even thought I saw that Flag at one point.

I was talking to a buddy who is a pretty talented mastering engineer and he was telling me how he measures his plugins, to see if they cut off bits.

I’ve heard many people say we can’t tell the different between 16 bit wavs and 24 bit/32 bit. Which you absolutely can, although sample rate plays more into that… shrug

Flip slide… I had a friend studying signal processing tell me, the dolby digital lossless has an incredibly high sample rate and is only 1 bit or 6 bit because it creates an amplified envelope because of the sample rate whatever, that means. (sampleRate is a double but not &buffer???)

To anyone wondering, I believe 24 bit 96Hz is the most effective format for sessions and only work in that. As a programmer I also believe a float is too grainy for professional audio, and long doubles may cause too many errors because of library support limitations at this time. So use doubles. Maybe even long doubles for digital summing…

Lastly I think the answer is here. Would you want a 4 bit chip processor in an analogue project, or a 8 bit processor? Thanks for reading this far!