# Digital 16-bit PCM audio signal scaling and STFT spectogram amplitude

I'm sampling a 16-bit PCM audio signal at 44100 Hz and I'm trying to understand the amplitude level of both the digital audio signal and its corresponding spectrum. The final goal is creating audio fingerprints for visualization and sound comparison. By now, I'm conscious I'm not calibrating my hardware so that dB scale is just a relative measurement to itself

First, for the 16-bit PCM audio signal I do understand the signal (Audio ADC lectures) ranges from -32768 to 32767, scaled to [-1 to 1) sig_scaled = sig_int16 / 32768 so that both -32768 and 32767 correspond to the maximum amplitude in db (0 db):

max_db = 20*np.log10(abs(+-1))= 0 dB


*Here, I do also understand it could be scaled to [0, 1) sig_scaled=(sig_int16-+32768)/65536

Meanwhile, the minimum one (~96db) corresponds to:

min_db = 20*np.log10(1/65536) ~= -96 dB

1. Which scaling is better [-1 to 1) or [0 to 1)?
2. How to avoid the "RuntimeWarning: divide by zero encountered in log10" when the ADC lecture or sig_int16 value is 0?

I think [-1,1) makes more sense since a sine wave goes from -1 to 1 but the obtained spectograms for both scales are similar.

Second, regarding the spectrum I've performed an stft on the [-1, 1) scaling, obtaining the following image (stft_db = 20*np.log10(stft/np.max(stft))): 1. What's the meaning of the -159.93 dB minimum and 0 dB maximum value? / Shouldn't the minimum value be -96 dB or bigger?
2. Which scaling ([-1, 1), [0, 1) or dB amplitud) should the STFT be performed on?

Thank you folks, let me know if you need more explanations or code!

1: Scaling

Audio corresponds to a pressure wave, so it is mostly symmetrical in amplitude. Therefore audio is normally represented as a signed value (with 0 average). [-1,1) is the standard representation.

You may offset your signal by 1/2 and represent the wave in the range [0,1), but this adds nothing but a huge component at 0 Hz in your spectrogram.

2: Warining when log(0)

You cannot calculate log(0). If that bothers you, create a function that produces a minimum value. Something like mylog(x) = log10(max(abs(x),1E-5)).

3: Small values in spectrogram

Values in spectrogram indicate energy per frequency bin. The more bins, the smaller values you may get there. And you may well have a 0 as result (see (2) above).

4: Signs again

Stick to signed values. See above.

• Thank you, in 2, for the mylog(x) function to work with arrays, it can't use abs() function but, for instance, numpy array indexing like def mylog10(x): x[x == 0] = 1E-6 return np.log10(x) – Pedro Martinez Lopez Nov 14 '17 at 17:53