How to determine a track's Bit depth

Here’s a code snippet that returns the bit depth of the selected track.

(Defun bit-depth (s)
(let ((s (if (arrayp s) (sim (aref s 0) (aref s 1))s))
(test-values '(10 11 20 23 29 31)))
(dolist (bits test-values )
(if (= (snd-maxsamp (integrate (diff s (quantize s 
(truncate (power 2 bits)))))) 0) 
(return bits)))
)); end bit-depth
(print  (case (bit-depth s)
(10 "8 bit unsigned")
(11 "12-bit")
('20 "16-bit integer")
(23 "24 bit integer")
(29 "30 bit compressed (mp3 etc.)")
(31 "32-bit integer")
(t"32 bit float")))

If you create a 32 bit float tone 32 bit will be returned.
Changing the format to 16 bit PCM returns 16 bit.
If you change back to 32 bit, 16 bit is still returned.
The tale’s morality:
Be careful, when changing the bit depth unless you’re already deaf,
in which case it doesn’t matter, how much went thru the shredder…

Audacity is notorious for internal quality manipulation. Please note it doesn’t have a Clip Inspector or Clip INFO in spite of numerous requests. You need to do that outside of Audacity.

Koz

Very nice. Thanks for sharing.

To make it more easily readable for the rest of us, I’ve reformatted your code with indentation (below).

;;; mix to mono if stereo
(defun mono (sig)
  (if (arrayp sig)
      (sim (aref sig 0) (aref sig 1))
      sig))

;;; test against powers of 2
(defun bit-test (sig bits)
  (snd-maxsamp 
    (integrate 
      (diff sig
        (quantize sig (truncate (power 2 bits)))))))

;;; find the bit depth
(defun bit-depth (sig)
  (let ((sig (mono sig))
        (test-values '(10 11 20 23 29 31)))
    (dolist (bits test-values)
            (if (= (bit-test sig bits) 0)
                (return bits)))))

;; run the test
(case (bit-depth s)
  (10 "8 bit unsigned")
  (11 "12-bit")
  (20 "16-bit integer")
  (23 "24 bit integer")
  (29 "30 bit compressed (mp3 etc.)")
  (31 "32-bit integer")
  (t "32 bit float"))

Thank you Steve for re-formatting the code (and removing the apostrophe before 20).
Its maybe worth mentioning that you don’t have to analyse the whole sound, 1100 samples should be enough. Just look that you don’t catch a silenced part. However, it can also work with silence, given that the sound was downsampled and dithering is on. Otherwise, “8-bit” will be returned.
It’s also interesting that imported mp3 sounds return 29, what would indicate a 30 bit Bit depth But mp3s do not have a constant bit depth. Thusthe the average seems to lie at (2^) 14.5.

The function snd-maxsamp should probably be replaced with the function peak as the Nyquist manual states that snd-maxsamp "will probably be removed in a future version (Nyquist Functions). The function “peak” must take a maxlen parameter, so that can be set to 1100 samples or whatever.

Well, “snd-maxsamp” has survived the last 10 years among other functions that should have been removed ever since.
therefore, I don’t have any qualms in using these functions. However, for the code above “peak” is preferable, since we can limit the calculation, if this is not already done by passing only a part of the sound.

10 years - doesn’t time fly while you’re having fun :slight_smile: but yes in this context I prefer “peak”.

Thanks again Robert. I think this snippet is still useful, so I’ve updated it for Audacity 3.x

;version 4
;type analyze

;;; mix to mono if stereo
(defun mono (sig)
  (if (arrayp sig)
      (sim (aref sig 0) (aref sig 1))
      sig))

;;; test against powers of 2
(defun bit-test (sig bits)
  (snd-maxsamp 
    (integrate 
      (diff sig
        (quantize sig (truncate (power 2 bits)))))))

;;; find the bit depth
(defun bit-depth (sig)
  (let ((sig (mono sig))
        (test-values '(10 11 20 23 29 31)))
    (dolist (bits test-values)
            (if (= (bit-test sig bits) 0)
                (return bits)))))

;; run the test
(case (bit-depth *track*)
  (10 "8 bit unsigned")
  (11 "12-bit")
  (20 "16-bit integer")
  (23 "24 bit integer")
  (29 "30 bit compressed (mp3 etc.)")
  (31 "32-bit integer")
  (t "32 bit float"))

Thanks Steve
Did you check if it returns the right values?
I have a vague collection that it might have been off by a tiny bit.

Robert

I’ve just been doing some testing.
It’s correct for 8-bit, 16-bit and 24-bit.
It’s difficult to test above 24-bit due to the many places that tiny amounts of rounding can occur.

An interesting thing that I discovered during testing, is that, at least on Linux, Audacity’s White Noise is 24-bit.

I wrote a different implementation for cross-checking. My version only deals with 8 / 16 / 24 bit integer, and assumes anything else is 32-bit float.
You can find it here: A Couple of Recording Questions - #8 by steve

Steve, the plugin you linked to, I see there is a defmacro in the code.
Stupid question, will this macro become global or only exist in the context of the plugin?

“DEFMACRO” is just a LISP expression, not related to Audacity itself. In its simplest form, the name of the macro is during runtime replaced by the code within the block below it.

It would certainly be interesting to be able to define or alter Audacity macros from within Nyquist.
However, the developers are worried about such structures that could potentially call themselves.
Robert

It remains local to the plug-in.

A “trick” that occasionally comes in useful: You can redefine any of Nyquist’s built-in macros and functions locally, so that they behave differently from the standard version. Again, this remains local to the specific script that is running.
A silly example,

(defun + (&rest args)
  (apply '- args))

(print (+ 10 2))

The limitation that Nyquist can’t call itself still exists, but Audacity now handles the limitation better. If a Nyquist macro command attempts to call a Nyquist plug-in, the command is skipped / ignored.
Related to this, you might be interested in looking at my “Macro Step Through” plug-in. The most recent version: Enhancement for Macros - #33 by steve

It remains local to the plug-in.

Thanks Steve.

You can redefine any of Nyquist’s built-in macros and functions locally

That can certainly come in handy for a few situations.

There’s an actual case in this older version of Audacity’s Noise Gate: https://github.com/audacity/audacity/blob/f16d74e74552155d6ea91a39accdc9ab77e4c44a/plug-ins/noisegate.ny
Scroll down to around line 139.

That override of Nyquist’s GATE function was because Nyquist had been updated, and the updated version limited the “floor” parameter to a minimum of 0.001 (-60 dB), which was too high for this effect. The Nyquist version of GATE has now been updated so that it supports values down to 0.00001 (-100 dB) so the override has now been removed from Audacity’s Noise Gate effect.

Steve wrote:

An interesting thing that I discovered during testing, is that, at least on Linux, Audacity’s White Noise is 24-bit.

I will see your 24 bits and raise you another 8 bits :smiley:
Screen Shot 2021-07-16 at 7.14.52 PM.png
White noise (Ver 2.3.1 on MacOS)

Generate the noise full scale (amplitude = 1) and try again.

If generated below amplitude = 1, the random samples are scaled to a lower level, so even if they were generated as 24-bit values, most of them will fall between 24-bit int values after scaling.

Generate the noise full scale (amplitude = 1) and try again.

Yep, then it’s down to 24 bit as well.

If generated below amplitude = 1, the random samples are scaled to a lower level

Does this only apply to noise (i.e. it’s a Nyquist thing) or any samples?
If it’s any samples (music, speech, etc), then surely the measurement is meaningless and the return value is random?

Nothing to do with Nyquist. It’s about Audacity’s implementation of a white noise generator.
This is the relevant line of code:

buffer[i] = mAmp * ((rand() / div) - 1.0f);

Audacity uses the C++ function “rand()” to generate random numbers, and normalizes them to a range of +/- 1
the value of “div” is derived from half of RAND_MAX, which is defined in C++ as:

RAND_MAX
Defined in header
#define RAND_MAX /implementation defined/

Expands to an integer constant expression equal to the maximum value returned by the function std::rand. This value is implementation dependent. It’s guaranteed that this value is at least 32767.

The noise is then scaled by “mAmp” which is set by the “Amplitude” setting in the Noise generator.

This means that the bit-deapth of full scale white noise produced in Audacity depends on the specific implementation of the C++ “rand()” function for the system it is running on.

This is different when using Nyquist’s white noise generator, which produces random 32-bit float samples (implemented in C rather than C++).