0
$\begingroup$

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:

Sonic Visualizer Result

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

Result of my Script

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


def load_normalized_audio_data(path: Path):
    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')
        # Read the converted file.
        frequency, data = wavfile.read(str(converted_file))
    finally:
        # Cleanup the converted file.
        temp_dir.cleanup()
    return frequency, data


def prepare_audio_data(path: Path):
    frequency, data = load_normalized_audio_data(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?

$\endgroup$
  • $\begingroup$ Why do you normalize by the sum of the window ? $\endgroup$ – Hilmar Apr 26 at 12:29
  • $\begingroup$ @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. $\endgroup$ – dsp_user Apr 26 at 13:22
  • $\begingroup$ I also tried to set the window length to e.g. 2^16, but this didn't made a huge difference. $\endgroup$ – Flovdis Apr 26 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.