Files
2026-07-22 13:23:22 +02:00

60 lines
2.0 KiB
Python

import librosa
# Monkeypatch for pyloudnorm which uses the deprecated scipy.signal.hann
import scipy.signal
import scipy.signal.windows
if not hasattr(scipy.signal, 'hann'):
scipy.signal.hann = scipy.signal.windows.hann
import pyloudnorm as pyln
import numpy as np
def analyze_audio(file_path: str) -> dict:
"""Analyzes an audio file and returns metadata."""
try:
# Load audio (mono, original sample rate to preserve frequency content)
y, sr = librosa.load(file_path, sr=None, mono=True)
# BPM
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
if isinstance(tempo, np.ndarray):
bpm = float(tempo[0])
else:
bpm = float(tempo)
# Key extraction using chroma (simplified root note extraction)
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)
key_idx = np.argmax(np.sum(chroma, axis=1))
keys = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
key = keys[key_idx]
# Energy (RMS)
rms = librosa.feature.rms(y=y)
energy = float(np.mean(rms))
# Spectral Centroid (Brightness)
cent = librosa.feature.spectral_centroid(y=y, sr=sr)
spectral_centroid = float(np.mean(cent))
# Onset Density (Danceability/Rhythm)
onset_env = librosa.onset.onset_strength(y=y, sr=sr)
onsets = librosa.onset.onset_detect(onset_envelope=onset_env, sr=sr)
duration_sec = len(y) / sr
onset_density = len(onsets) / duration_sec if duration_sec > 0 else 0
# LUFS
meter = pyln.Meter(sr) # create BS.1770 meter
lufs = meter.integrated_loudness(y)
return {
"bpm": bpm,
"key": key,
"energy": energy,
"spectral_centroid": spectral_centroid,
"onset_density": float(onset_density),
"lufs": float(lufs)
}
except Exception as e:
print(f"Error analyzing {file_path}: {e}")
return None