-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspectrogram.py
More file actions
91 lines (77 loc) · 3.08 KB
/
Copy pathspectrogram.py
File metadata and controls
91 lines (77 loc) · 3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from spectrogram.config_reader import get_spectrogram_property
def show_spectrogram(file_path: str, title: str) -> None:
"""
Chart the waveform and spectrogram derived from that waveform
"""
# Load configuration properties
n_fft = get_spectrogram_property("n_fft")
hop_length = get_spectrogram_property("hop_length")
# Set a default title if one isn't set
if not title:
title = f"Spectrogram for {Path(file_path).name.upper()}"
# Load the waveform and determine the sample rate. The result is a 1D array of
# amplitude values over time and the number of samples per second. Time is implied
# by index position in the array divided by sample rate
y, sr = librosa.load(file_path, sr=None)
# Compute Short-Time Fourier Transform: break audio into overlapping time windows and
# extract frequency content for each moment. If the input audio has a sample rate of
# 11025, then n_fft = 2048 splits the audio into 2048/11025 s frames i.e. ~0.186 s.
# As a general rule:
#
# Higher n_fft gives better frequency resolution, worse time resolution
# Lower n_fft gives better time resolution, worse frequency resolution
#
# Hop length controls spacing between frames and a 256 the hop is 256/11025 s i.e. ~23 ms
# This is, effectively, the sampling frequency for producing the spectrogram
D = librosa.stft(y, n_fft=n_fft, hop_length=hop_length, window="hann")
# This converts the results of the transform from amplitude to decibels (log scale).
# This yields a 2D array of [frequency × time] in decibels with each point in that array
# answering the question "How string is this frequency at this moment in time?"
S = librosa.amplitude_to_db(np.abs(D), ref=np.max)
# Create the figure
fig = plt.figure(figsize=(12, 6))
fig.suptitle(title, fontsize=14, y=0.93)
gs = fig.add_gridspec(
2, 2,
width_ratios=[20, 1],
height_ratios=[1, 3],
hspace=0.01,
wspace=0.15
)
# Add the charts for the waveform and spectrogram
ax_wave = fig.add_subplot(gs[0, 0])
ax_spec = fig.add_subplot(gs[1, 0], sharex=ax_wave)
cax = fig.add_subplot(gs[1, 1])
fig.add_subplot(gs[0, 1]).axis("off")
# Chart the waveform
librosa.display.waveshow(y, sr=sr, ax=ax_wave)
# Chart the spectrogram
img = librosa.display.specshow(
S,
sr=sr,
hop_length=hop_length,
x_axis="time",
y_axis="hz",
cmap="magma",
vmin=-95,
vmax=-35,
ax=ax_spec
)
# Force both panels to the real duration
duration = len(y) / sr
ax_wave.set_xlim(0, duration)
ax_spec.set_xlim(0, duration)
ax_wave.margins(x=0)
ax_spec.margins(x=0)
# Add the colour bar
fig.colorbar(img, cax=cax, format="%+2.0f dB")
# Set labels
ax_spec.set_xlabel("Time")
ax_spec.set_ylabel("Hz")
ax_wave.tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
plt.show()