Skip to content

Instantly share code, notes, and snippets.

@JanWilczek
Created March 29, 2024 18:53
Show Gist options
  • Select an option

  • Save JanWilczek/c2103897d9a93fce0b02b690ca87d36d to your computer and use it in GitHub Desktop.

Select an option

Save JanWilczek/c2103897d9a93fce0b02b690ca87d36d to your computer and use it in GitHub Desktop.
Easily plot the magnitude spectrum of any audio signal and save it as a png file using Python and Matplotlib.Pyplot library. Includes styling the figure so that it looks reasonably well. You can use decibels full scale (dBFS) for the magnitude axis and a logarithmic frequency axis with ISO-standardized frequencies. Feel free to copy, paste & mod…
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
import soundfile as sf
import librosa
COLOR = "#ef7600"
IMG_OUTPUT_PATH = Path("img")
SAVE_PARAMS = {"dpi": 300, "bbox_inches": "tight", "transparent": True}
XTICKS = np.array([31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000])
XTICK_LABELS = np.array(["31.25", "62.5", "125", "250", "500", "1k", "2k", "4k", "8k"])
plt.rcParams.update({"font.size": 20})
def save_spectrum(output_path):
output_path.parent.mkdir(parents=True, exist_ok=True)
plt.savefig(output_path, **SAVE_PARAMS)
def plot_spectrum_and_save(frequencies, magnitude_spectrum, output_path: Path):
plt.figure(figsize=(12, 6))
plt.plot(frequencies, magnitude_spectrum, COLOR)
xlim = [frequencies[0], frequencies[-1]]
plt.xlim(xlim)
plt.xlabel("frequency [Hz]")
plt.hlines(0, xlim[0], xlim[1], colors="k")
plt.xticks(None, None)
plt.yticks([])
plt.ylabel("magnitude")
ax = plt.gca()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
save_spectrum(output_path)
plt.close()
def plot_spectrum_db_and_save(frequencies, magnitude_spectrum, output_path: Path):
plt.figure(figsize=(12, 6))
plt.plot(frequencies, magnitude_spectrum, COLOR)
plt.xlim([0, frequencies[-1]])
plt.ylim([-60, 0])
plt.grid()
plt.xlabel("frequency [Hz]")
plt.ylabel("magnitude [dBFS]")
ax = plt.gca()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
save_spectrum(output_path)
plt.close()
def plot_spectrum_db_in_octaves_and_save(
frequencies, magnitude_spectrum, output_path: Path
):
plt.figure(figsize=(12, 6))
plt.semilogx(frequencies, magnitude_spectrum, COLOR)
plt.ylim([-60, 0])
plt.xticks(XTICKS, XTICK_LABELS)
min_x = 29
plt.xlim([min_x, frequencies[-1]])
plt.grid()
plt.xlabel("frequency [Hz]")
plt.ylabel("magnitude [dBFS]")
ax = plt.gca()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
# plt.show() # closes the figure
save_spectrum(output_path)
plt.close()
def main():
signal, sample_rate = sf.read(
Path(".") / "data" / "LibriSpeech-84-121123-0001.flac"
)
print(f"Signal's sample rate: {sample_rate} Hz.")
magnitude_spectrum = np.abs(np.fft.rfft(signal))
frequencies = np.fft.rfftfreq(signal.shape[0], 1 / sample_rate)
plot_spectrum_and_save(
frequencies,
magnitude_spectrum,
IMG_OUTPUT_PATH / "speech_magnitude_spectrum.png",
)
normalized_magnitude_spectrum = magnitude_spectrum / np.amax(magnitude_spectrum)
magnitude_spectrum_db = librosa.amplitude_to_db(normalized_magnitude_spectrum)
plot_spectrum_db_and_save(
frequencies,
magnitude_spectrum_db,
IMG_OUTPUT_PATH / "speech_magnitude_spectrum_db.png",
)
plot_spectrum_db_in_octaves_and_save(
frequencies,
magnitude_spectrum_db,
IMG_OUTPUT_PATH / "speech_magnitude_spectrum_db_in_octaves.png",
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment