Spaces:
Sleeping
Sleeping
File size: 2,763 Bytes
cde4ff8 | 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 | import librosa
import numpy as np
from scipy import stats
import warnings
# Suppress librosa's PySoundFile warnings for mp3s
warnings.filterwarnings('ignore')
def compute_statistics(feature_matrix):
"""
Calculates the 7 FMA statistics across a feature matrix.
Order: kurtosis, max, mean, median, min, skew, std.
"""
kurtosis = stats.kurtosis(feature_matrix, axis=1)
maximum = np.max(feature_matrix, axis=1)
mean = np.mean(feature_matrix, axis=1)
median = np.median(feature_matrix, axis=1)
minimum = np.min(feature_matrix, axis=1)
skewness = stats.skew(feature_matrix, axis=1)
std = np.std(feature_matrix, axis=1)
return np.concatenate([kurtosis, maximum, mean, median, minimum, skewness, std])
def extract_audio_features(file_path):
"""
Loads audio, extracts 518 features, and returns frontend metrics.
"""
# 1. Load exactly 30 seconds at 22050 Hz
y, sr = librosa.load(file_path, sr=22050, duration=30.0)
# 2. Extract Base Features
stft = np.abs(librosa.stft(y, n_fft=2048, hop_length=512))
chroma_stft = librosa.feature.chroma_stft(S=stft, sr=sr)
chroma_cqt = librosa.feature.chroma_cqt(y=y, sr=sr)
chroma_cens = librosa.feature.chroma_cens(y=y, sr=sr)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
rmse = librosa.feature.rms(S=stft)
spec_cent = librosa.feature.spectral_centroid(S=stft, sr=sr)
spec_bw = librosa.feature.spectral_bandwidth(S=stft, sr=sr)
spec_contrast = librosa.feature.spectral_contrast(S=stft, sr=sr)
spec_rolloff = librosa.feature.spectral_rolloff(S=stft, sr=sr)
tonnetz = librosa.feature.tonnetz(y=librosa.effects.harmonic(y), sr=sr)
zcr = librosa.feature.zero_crossing_rate(y)
# 3. Compute Statistics (Strict Order Required)
features = []
feature_list = [chroma_cens, chroma_cqt, chroma_stft, mfcc, rmse,
spec_bw, spec_cent, spec_contrast, spec_rolloff, tonnetz, zcr]
for feature in feature_list:
features.append(compute_statistics(feature))
final_feature_vector = np.concatenate(features)
# 4. Extract clean frontend metrics for the p5.js Mandala
# 4. Extract clean frontend metrics for the p5.js Mandala
bpm, _ = librosa.beat.beat_track(y=y, sr=sr)
# SAFELY handle the Librosa version difference (extracts the number if it's trapped in an array)
bpm_value = bpm[0] if isinstance(bpm, np.ndarray) else bpm
frontend_metrics = {
"bpm": round(float(bpm_value), 2),
"energy_rmse": round(float(np.mean(rmse)), 4),
"brightness_centroid": round(float(np.mean(spec_cent)), 2),
"mfcc_vector": [round(float(x), 2) for x in np.mean(mfcc, axis=1)[:3]]
}
return final_feature_vector, frontend_metrics |