In a last article, we explained the mathematical effect of quantization and what the resulting quantization noise is. In this article, we will hear, how the quantization noise actually sounds. As a teaser, listen to the following:
# For running this code, the code snippets below need to be run beforehand
display(HTML("Original signal:" + Audio(data=data_music, rate=rate)._repr_html_()))
showQuantization(data_music, U=1,bits=4, showSignals=False);
Clearly, we hear a significant noise floor below the original music signal. Let's look deeper into what actually happens. First, we define a function to load some audio file from the internet:
def loadAudio(url, start, length):
R = requests.get(url)
with open("sound.mp3", "wb") as f:
f.write(R.content)
!ffmpeg -y -i sound.mp3 sound.wav > /dev/null 2>&1
rate, data = wavfile.read("sound.wav")
if len(data.shape) > 1: # stereo to mono conversion
data = data.sum(axis=1)
data = (1.0 * data / abs(data).max()).astype(np.float32)
dataPart = data[rate*start+np.arange(min(rate*length, len(data)))]
targetRate = 10000 # Resample signal to 10kHz sampling rate
targetSamples = int(len(dataPart) * targetRate / rate)
resampled = signal.resample(dataPart, targetSamples)
return targetRate, resampled / abs(resampled).max()
# Utility function two display two audios side by side in the notebook
def audioSideBySide(name1, audio1, name2, audio2):
text = '%s %s %s %s
' % (name1, name2, audio1._repr_html_(), audio2._repr_html_())
display(HTML(text))
Then, we load some music from the internet and extract a portion of 10 seconds length out of it:
url_music = "http://www.scientificinvesting.eu/a/Mozart%20-%20Symphony%20n.10%20K.74%20in%20G%20-%201%20Allegro.mp3"
rate_music, data_music = loadAudio(url_music, 40, 10)
rate = rate_music
Next, we define the functions for calculation of the quantization thresholds and for performing the actual quantization. Sure, we have shamelessly copied them from our previous article:
# Calculate the quantization levels with a uniform quantizer
def calcLevels(U, b, quantization_type):
N_levels = 2**b
delta = 2*U / N_levels
if quantization_type == 'mid-rise':
levels = -U + delta/2 + np.arange(N_levels) * delta
elif quantization_type == 'mid-tread':
levels = -U + np.arange(N_levels) * delta
else:
raise RuntimeError("Unknown quantization type!")
return levels
# Map the input array x to the nearest values in S
def quantize(x, S):
X = x.reshape((-1,1))
S = S.reshape((1,-1))
dists = abs(X-S)
nearestIndex = dists.argmin(axis=1)
quantized = S.flat[nearestIndex]
return quantized.reshape(x.shape)
Let us now define a convenience function that performs the quantization of a signal, shows the resulting signals and creates the audio objects:
def showQuantization(audio, U, bits, quantization_type='mid-rise', showNoise=True, showSignals=True):
S = calcLevels(U=U, b=bits, quantization_type=quantization_type)
quantized = quantize(audio, S) # Perform quantization
q_noise = audio - quantized # Calculate quantization noise
P_signal = sum(abs(audio**2)) # Calculate SNR in dB
P_noise = sum(abs(q_noise**2))
SNR = 10*np.log10(P_signal/P_noise)
audioSideBySide("Quantized to q=%d bits" % bits, Audio(data=quantized, rate=rate),
"Quantization Noise", Audio(data=q_noise, rate=rate))
t = np.arange(len(audio)) / rate
if showSignals:
plt.plot(t, audio, label='Original')
plt.plot(t, quantized, label='Quantized to q=%d bits' % bits)
if showNoise:
plt.plot(t, q_noise, label='Quantization Noise')
return t, quantized, q_noise
Now, we are ready to listen to the quantized music and also look at the resulting quantized signals. First, listen to the original signal once again:
Audio(data=data_music, rate=rate)