bpiyush's picture
Upload folder using huggingface_hub
eafbf97 verified
raw
history blame
6.28 kB
"""Audio utils"""
import librosa
import numpy as np
import matplotlib.pyplot as plt
def load_audio(audio_path: str, sr: int = None, max_duration: int = 10., start: int = 0, stop: int = None):
"""Loads audio and pads/trims it to max_duration"""
data, sr = librosa.load(audio_path, sr=sr)
if stop is not None:
start = int(start * sr)
stop = int(stop * sr)
data = data[start:stop]
# Convert to mono
if len(data.shape) > 1:
data = np.mean(data, axis=1)
n_frames = int(max_duration * sr)
if len(data) > n_frames:
data = data[:n_frames]
elif len(data) < n_frames:
data = np.pad(data, (0, n_frames - len(data)), "constant")
return data, sr
# def compute_spectrogram(data: np.ndarray, sr: int):
# D = librosa.stft(data) # STFT of y
# S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
# return S_db
def compute_spec_freq_mean(S_db: np.ndarray, eps=1e-5):
# Compute mean of spectrogram over frequency axis
S_db_normalized = (S_db - S_db.mean(axis=1)[:, None]) / (S_db.std(axis=1)[:, None] + eps)
S_db_over_time = S_db_normalized.sum(axis=0)
return S_db_over_time
def process_audiofile(audio_path, functions=["load_audio", "compute_spectrogram", "compute_spec_freq_mean"]):
"""Processes audio file with a list of functions"""
data, sr = load_audio(audio_path)
for function in functions:
if function == "load_audio":
pass
elif function == "compute_spectrogram":
data = compute_spectrogram(data, sr)
elif function == "compute_spec_freq_mean":
data = compute_spec_freq_mean(data)
else:
raise ValueError(f"Unknown function {function}")
return data
"""PyDub's silence detection is based on the energy of the audio signal."""
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
class SilenceDetector:
def __init__(self, silence_thresh=-36) -> None:
self.silence_thresh = silence_thresh
def __call__(self, audio_path: str, start=None, end=None):
import pydub
from pydub.utils import db_to_float
try:
waveform = pydub.AudioSegment.from_file(audio_path)
except:
print("Error loading audio file: ", audio_path)
return 100.0
start_ms = int(start * 1000) if start else 0
end_ms = int(end * 1000) if end else len(waveform)
waveform = waveform[start_ms:end_ms]
# convert silence threshold to a float value (so we can compare it to rms)
silence_thresh = db_to_float(self.silence_thresh) * waveform.max_possible_amplitude
if waveform.rms == 0:
return 100.0
silence_prob = sigmoid((silence_thresh - waveform.rms) / waveform.rms)
# return waveform.rms <= silence_thresh
return np.round(100 * silence_prob, 2)
def frequency_bin_to_value(bin_index, sr, n_fft):
return int(bin_index * sr / n_fft)
def time_bin_to_value(bin_index, hop_length, sr):
return (bin_index) * (hop_length / sr)
def add_time_annotations(ax, nt_bins, hop_length, sr, skip=50):
# Show time (s) values on the x-axis
t_bins = np.arange(nt_bins)
t_vals = np.round(np.array([time_bin_to_value(tb, hop_length, sr) for tb in t_bins]), 1)
try:
ax.set_xticks(t_bins[::skip], t_vals[::skip])
except:
pass
ax.set_xlabel("Time (s)")
def add_freq_annotations(ax, nf_bins, sr, n_fft, skip=50):
f_bins = np.arange(nf_bins)
f_vals = np.array([frequency_bin_to_value(fb, sr, n_fft) for fb in f_bins])
try:
ax.set_yticks(f_bins[::skip], f_vals[::skip])
except:
pass
# ax.set_yticks(f_bins[::skip])
# ax.set_yticklabels(f_vals[::skip])
ax.set_ylabel("Frequency (Hz)")
def show_single_spectrogram(
spec,
sr,
n_fft,
hop_length,
ax=None,
fig=None,
figsize=(10, 2),
cmap="viridis",
colorbar=True,
show=True,
format='%+2.0f dB',
xlabel='Time (s)',
ylabel="Frequency (Hz)",
title=None,
show_dom_freq=False,
):
if ax is None:
fig, ax = plt.subplots(1, 1, figsize=figsize)
axim = ax.imshow(spec, origin="lower", cmap=cmap)
# Show frequency (Hz) values on y-axis
nf_bins, nt_bins = spec.shape
if "frequency" in ylabel.lower():
# Add frequency annotation
add_freq_annotations(ax, nf_bins, sr, n_fft)
# Add time annotation
add_time_annotations(ax, nt_bins, hop_length, sr)
ax.set_title(title)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
if colorbar:
fig.colorbar(axim, ax=ax, orientation='vertical', fraction=0.01, format=format)
if show_dom_freq:
fmax = spec.argmax(axis=0)
ax.scatter(np.arange(spec.shape[1]), fmax, color="white", s=0.2)
if show:
plt.show()
def compute_spectrogram(y, n_fft, hop_length, margin, n_mels=None):
# STFT
D = librosa.stft(y, n_fft=n_fft, hop_length=hop_length)
# Run HPSS
S, _ = librosa.decompose.hpss(D, margin=margin)
# DB
S = librosa.amplitude_to_db(np.abs(S), ref=np.max)
if n_mels is not None:
S = librosa.feature.melspectrogram(S=S, n_mels=n_mels)
return S
def show_spectrogram(S, sr, n_fft=512, hop_length=256, figsize=(10, 3), n_mels=None, ax=None, show=True):
if ax is None:
fig, ax = plt.subplots(1, 1, figsize=figsize)
y_axis = "mel" if n_mels is not None else "linear"
librosa.display.specshow(
S,
sr=sr,
hop_length=hop_length,
n_fft=n_fft,
y_axis=y_axis,
x_axis='time',
ax=ax,
)
ax.set_title("LogSpectrogram" if n_mels is None else "LogMelSpectrogram")
if show:
plt.show()
def show_frame_and_spectrogram(frame, S, sr, figsize=(12, 4), show=True, axes=None, **spec_args):
if axes is None:
fig, axes = plt.subplots(1, 2, figsize=figsize, gridspec_kw={"width_ratios": [0.2, 0.8]})
ax = axes[0]
ax.imshow(frame)
ax.set_xticks([])
ax.set_yticks([])
ax = axes[1]
show_spectrogram(S=S, sr=sr, ax=ax, show=False, **spec_args)
plt.tight_layout()
if show:
plt.show()