# Why is there a difference in the spectrum of an audio file between Sonic Visualizer and my Python script?

I am working on a script which is creating a spectrum analysis from an audio file using SciPy and NumPy. Before I started, I analyzed the file using Sonic Visualizer, which got me the following result:

Now I tried to reproduce this result using my Python Script, but get a different result:

Everything looks right, except the scale of the dB values. At 10Hz, Sonic Visualizer is at -40db and my Script is at -80 dB. So I assume, there is a problem in my script.

A minimal version of the script looks like this:

import subprocess
import tempfile
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
from scipy.io import wavfile
from scipy.signal import savgol_filter

SOX_CMD = 'sox'

def db_fft(data, frequency):
data_length = len(data)
win = np.hamming(data_length)
data = data * win
sp = np.fft.rfft(data)
freq = np.arange((data_length / 2) + 1) / (float(data_length) / frequency)
s_mag = np.abs(sp) * 2 / np.sum(win)
s_dbfs = 20 * np.log10(s_mag)
return freq, s_dbfs

temp_dir = tempfile.TemporaryDirectory()
try:
converted_file = Path(temp_dir.name) / 'input_file.wav'
args = [SOX_CMD, str(path), '-e', 'float', '-b', '32', '-c', '1', str(converted_file),
'trim', '0', '10']
subprocess.run(args, check=True, capture_output=True, encoding='utf-8')
finally:
# Cleanup the converted file.
temp_dir.cleanup()
return frequency, data

def prepare_audio_data(path: Path):
labels, s_dbfs = db_fft(data, frequency)
flat_data = savgol_filter(s_dbfs, 601, 3)
return labels, s_dbfs, flat_data

audio_file = Path('test.wav')
x_labels, data, flat_data = prepare_audio_data(audio_file)
plt.style.use('seaborn-whitegrid')
plt.figure(dpi=150, figsize=(16, 9))
x_labels = x_labels[0:len(data)]
plt.semilogx(x_labels, data, alpha=0.4, color='tab:blue', label='Spectrum')
plt.semilogx(x_labels, flat_data, color='tab:blue', label='Spectrum (with filter)')
plt.grid(True)
plt.title(audio_file.name)
plt.ylim([-160, 0])
plt.xlim([10, 10000])
plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude [dB]')
plt.grid(True, which="both")
target_name = audio_file.parent / (audio_file.stem + '.png')
plt.savefig(str(target_name))
print('Success!')


The script converts any audio file into a 32bit float format, trimming the first 10 seconds of the file using the SoX command. You can ignore this part. Just assume the input file is in 32bit float format.

I assume, the problem is somewhere in the db_fft function.

Where is the problem with my script, why I get a different result?

• Why do you normalize by the sum of the window ? – Hilmar Apr 26 '20 at 12:29
• @Hilmar, that's fine. Normalizing by dividing by the sum of the window takes into account the specifics of the window used (its area), as opposed to just dividing by N. – dsp_user Apr 26 '20 at 13:22
• I also tried to set the window length to e.g. 2^16, but this didn't made a huge difference. – Flovdis Apr 26 '20 at 13:25