Reputation: 8925
I use audacity to generate a 10Hz tone with 48KHz samplerate and 1 seconds duration. then load it with below script to plot FFT graph:
from scipy.io import wavfile
from scipy.fftpack import fft, fftfreq
import matplotlib.pyplot as plt
from pydub import AudioSegment
import numpy as np
wav_filename = "\\test\\10Hz.wav"
samplerate, data = wavfile.read(wav_filename)
total_samples = len(data)
limit = int((total_samples /2)-1)
fft_abs = abs(fft(data))*2/total_samples
fft_db = 20*np.log10(fft_abs/32760)
freqs = fftfreq(total_samples,1/samplerate)
# plot the frequencies
plt.plot(freqs[:limit], fft_db[:limit])
plt.xscale('log',basex=10)
plt.title("Frequency spectrum")
plt.xlabel('Hz')
plt.ylabel('amplitude')
plt.show()
But if I use audacity to view the spectrum graph, it's pretty good.
How should I improve my script to get better FFT drawing?
Upvotes: 3
Views: 1261
Reputation: 9817
That particular pattern of the frequency spectrum of the WAV file is generated by the noise-shaped dither applied by Audacity to mitigate the distortion created by quantization.
The signal is stored as signed 16-bit integers in the WAV file. This quantization makes the signal quite innacurate: 2^15 is 32768 : relative errors of 0.5/32768 are very likely and computing the DFT may increase it. That is about 1e-5, or -96dB. Hence, any frequency below 96dB can be discarted as quantization noise if the values of the signal are carelessly cast to the nearest integer.
Nevertheless, the frequency spectrum of the exported WAV file looks very nice, as all frequencies feature an amplitude lower than -100dB, the only exception being the frequency of the sine wave, which features an amplitude near 0dB. The question is to be rephrased: how does Audacity acheive such an accurate spectrum for the WAV file?
The exported values are optimized to improve the accuracy of the spectrum. In fact, the accuracy of the individual exported values is less precise than casting floating values to integer. I generated a sine wave of amplitude 0.8 in audacity and exported it as WAV signed 16bit PCM. The quantization error can be computed as:
x=np.linspace(0,1,len(data),endpoint=False)
data2=np.sin(10*2*np.pi*x)
data=data.astype(np.double)
plt.plot(x, data-0.8*32768*data2, label="data-sin(x)")
#plt.xscale('log',basex=10)
plt.title("signal")
plt.xlabel('time, s')
plt.ylabel('amplitude')
plt.show()
The generated plot of the quantization error:
If the signal values were simply cast to nearest integer and poured in the WAV file, the generated error would have been lower than 0.5. The actual pointwise error is about 5.
A noise-shaped dither is considered in Audacidy to improve the dynamic range of quantizated signals by adding a high frequency noise hardly heard by human hears. there, there and there.
You may choose the dither in Audacity or even disable it. Go into Preferences=> Quality! If no dither is considered, the pointwise error in the real space is smaller:
But the quantization error on the spectrum is higher:
A rectangular dither could be used to retreive something looking like a white noise: it may proove the best choice for your use, as the error on the spectrum is about -125dB and the pointwise error is +-1.
Upvotes: 4