| name | mfcc-spectrogram-analysis |
| description | Analyse spectrale audio — MFCC, spectrogramme Mel, chroma, spectral features, STFT, CQT, extraction de caractéristiques pour ML/ASR/classification musicale. |
| tags | ["audio","dsp","mfcc","spectrogram","features","librosa","feature-extraction","signal-analysis"] |
| platforms | ["linux","macos","windows"] |
| related_skills | ["audio-processing","asr-reconnaissance-vocale","music-generation"] |
MFCC & Spectrogram Analysis — Analyse Spectrale et Extraction de Caractéristiques
Guide complet des représentations temps-fréquence, extraction de caractéristiques audio pour ML, ASR, classification musicale et analyse acoustique.
1. Fondamentaux : Transformations Temps-Fréquence
1.1 STFT (Short-Time Fourier Transform)
import librosa
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
audio, sr = librosa.load("audio.wav", sr=22050)
D = librosa.stft(
audio,
n_fft=2048,
hop_length=512,
win_length=2048,
window='hann',
center=True
)
magnitude_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
phase = np.angle(D)
librosa.display.specshow(magnitude_db, sr=sr, hop_length=512, x_axis='time', y_axis='hz')
plt.colorbar(format='%+2.0f dB')
plt.title('Spectrogramme STFT')
plt.tight_layout()
1.2 Paramètres de la STFT
| Paramètre | Valeur typique | Effet |
|---|
n_fft | 2048 (93ms @ 22kHz) | Résolution fréquentielle Δf = sr/n_fft |
hop_length | 512 (23ms) | Résolution temporelle : Δt = hop/sr |
window | hann | Compromis résolution/leakage spectral |
center | True | Trames centrées (pas de décalage) |
Compromis temps-fréquence : n_fft large → haute résolution fréquentielle, faible résolution temporelle. n_fft petit → l'inverse.
1.3 CQT (Constant-Q Transform)
C = librosa.cqt(
audio,
sr=sr,
fmin=librosa.note_to_hz('C1'),
n_bins=84,
bins_per_octave=12,
filter_scale=1
)
C_db = librosa.amplitude_to_db(np.abs(C), ref=np.max)
C_half = librosa.hybrid_cqt(audio, sr=sr, fmin=32.7, n_bins=84)
librosa.display.specshow(C_db, sr=sr, x_axis='time', y_axis='cqt_note')
plt.title('Spectrogramme CQT (Constant-Q)')
2. Mel Spectrogramme
2.1 Génération
mel_spec = librosa.feature.melspectrogram(
y=audio,
sr=sr,
n_fft=2048,
hop_length=512,
win_length=2048,
window='hann',
n_mels=128,
fmin=0,
fmax=sr/2,
htk=False,
power=2.0
)
mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)
librosa.display.specshow(mel_spec_db, sr=sr, hop_length=512, x_axis='time', y_axis='mel')
plt.colorbar(format='%+2.0f dB')
plt.title('Mel Spectrogramme (128 bandes)')
2.2 Échelle Mel — Formule
Formule standard : Mel(f) = 2595 × log10(1 + f/700)
Formule HTK : Mel(f) = 1127 × ln(1 + f/700)
L'échelle Mel est linéaire < 1000 Hz, logarithmique au-dessus, imitant la perception humaine des hauteurs.
2.3 Mel spectrogramme inversé
S = librosa.db_to_power(mel_spec_db)
y_reconstructed = librosa.feature.inverse.mel_to_audio(
S,
sr=sr,
n_fft=2048,
hop_length=512,
win_length=2048,
window='hann',
n_iter=32,
length=len(audio)
)
stft_reconstructed = librosa.feature.inverse.mel_to_stft(
S,
sr=sr,
n_fft=2048,
power=2.0
)
3. MFCC (Mel-Frequency Cepstral Coefficients)
3.1 Extraction
mfccs = librosa.feature.mfcc(
y=audio,
sr=sr,
n_mfcc=13,
n_fft=2048,
hop_length=512,
win_length=2048,
window='hann',
n_mels=40,
fmin=0,
fmax=sr/2,
dct_type=2,
norm='ortho',
lifter=0,
)
print(f"Forme : {mfccs.shape}")
mfccs_delta = librosa.feature.delta(mfccs)
mfccs_delta2 = librosa.feature.delta(mfccs, order=2)
mfccs_full = np.vstack([mfccs, mfccs_delta, mfccs_delta2])
librosa.display.specshow(mfccs, sr=sr, hop_length=512, x_axis='time')
plt.ylabel('Coefficient MFCC')
plt.title('MFCC (13 coefficients)')
3.2 Pipeline de prétraitement pour ASR
class ASRFeatureExtractor:
"""Extraction de caractéristiques pour ASR (type Kaldi)."""
def __init__(self, sr=16000, n_mfcc=13, n_mels=23, frame_length=25, frame_shift=10):
self.sr = sr
self.n_mfcc = n_mfcc
self.n_mels = n_mels
self.n_fft = int(sr * frame_length / 1000)
self.hop_length = int(sr * frame_shift / 1000)
def extract(self, audio):
audio = np.append(audio[0], audio[1:] - 0.97 * audio[:-1])
mel = librosa.feature.melspectrogram(
y=audio, sr=self.sr,
n_fft=self.n_fft,
hop_length=self.hop_length,
n_mels=self.n_mels,
fmin=0, fmax=self.sr/2,
power=2.0
)
log_mel = np.log(mel + 1e-10)
mfcc = librosa.feature.mfcc(
S=log_mel, n_mfcc=self.n_mfcc,
dct_type=, norm=
)
mfcc = (mfcc - np.mean(mfcc, axis=, keepdims=)) / (np.std(mfcc, axis=, keepdims=) + )
delta = librosa.feature.delta(mfcc)
delta2 = librosa.feature.delta(mfcc, order=)
np.vstack([mfcc, delta, delta2])
extractor = ASRFeatureExtractor()
features = extractor.extract(audio)
3.3 MFCC pour classification musicale
def extract_music_features(audio_path):
"""Extrait MFCC + features musicaux pour classification de genre."""
audio, sr = librosa.load(audio_path, sr=22050, duration=30)
mfcc = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=20, n_mels=128)
mfcc_mean = np.mean(mfcc, axis=1)
mfcc_std = np.std(mfcc, axis=1)
mfcc_skew = np.mean(((mfcc - mfcc_mean.reshape(-1, 1)) / (mfcc_std.reshape(-1, 1) + 1e-10)) ** 3, axis=1)
chroma = librosa.feature.chroma_stft(y=audio, sr=sr)
chroma_mean = np.mean(chroma, axis=1)
spectral_centroid = librosa.feature.spectral_centroid(y=audio, sr=sr)
spectral_bandwidth = librosa.feature.spectral_bandwidth(y=audio, sr=sr)
spectral_rolloff = librosa.feature.spectral_rolloff(y=audio, sr=sr)
spectral_contrast = librosa.feature.spectral_contrast(y=audio, sr=sr)
zcr = librosa.feature.zero_crossing_rate(audio)
tempo, _ = librosa.beat.beat_track(y=audio, sr=sr)
feature_vector = np.concatenate([
mfcc_mean, mfcc_std, mfcc_skew,
chroma_mean,
[np.mean(spectral_centroid), np.std(spectral_centroid)],
[np.mean(spectral_bandwidth), np.std(spectral_bandwidth)],
[np.mean(spectral_rolloff), np.std(spectral_rolloff)],
[np.mean(spectral_contrast), np.std(spectral_contrast)],
[np.mean(zcr), np.std(zcr)],
[tempo]
])
return feature_vector
4. Caractéristiques Spectrales Avancées
4.1 Spectral features
centroid = librosa.feature.spectral_centroid(y=audio, sr=sr)
bandwidth = librosa.feature.spectral_bandwidth(y=audio, sr=sr, p=2)
rolloff = librosa.feature.spectral_rolloff(y=audio, sr=sr, roll_percent=0.85)
contrast = librosa.feature.spectral_contrast(y=audio, sr=sr, n_bands=6, fmin=200.0)
flatness = librosa.feature.spectral_flatness(y=audio, sr=sr, amin=1e-10, power=2.0)
tonnetz = librosa.feature.tonnetz(y=audio, sr=sr, chroma=None)
4.2 Chroma Features
chroma_stft = librosa.feature.chroma_stft(y=audio, sr=sr, n_chroma=12, norm=2)
chroma_cqt = librosa.feature.chroma_cqt(y=audio, sr=sr, n_chroma=12)
chroma_cens = librosa.feature.chroma_cens(y=audio, sr=sr, n_chroma=12)
chroma_notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
chroma_mean = np.mean(chroma_cqt, axis=1)
for note, energy in zip(chroma_notes, chroma_mean):
print(f"{note:>2} : {'█' * int(energy * 50)}")
4.3 Rhythmic Features
tempo, beats = librosa.beat.beat_track(y=audio, sr=sr)
print(f"Tempo : {tempo:.1f} BPM ({len(beats)} beats)")
ibi = np.diff(beats) / sr * 1000
print(f"IBI moyen : {np.mean(ibi):.1f}ms, std : {np.std(ibi):.1f}ms")
onset_env = librosa.onset.onset_strength(y=audio, sr=sr)
pulse = librosa.beat.plp(y=audio, sr=sr)
print(f"PLP max : {np.max(pulse):.3f}, min : {np.min(pulse):.3f}")
tempogram = librosa.feature.tempogram(y=audio, sr=sr, onset_envelope=onset_env)
5. Extraction pour Machine Learning
5.1 TensorFlow / PyTorch : Spectrogram dataset
import torch
import torch.nn as nn
import torchaudio
waveform, sr = torchaudio.load("audio.wav")
mel_transform = torchaudio.transforms.MelSpectrogram(
sample_rate=sr,
n_fft=2048,
hop_length=512,
n_mels=128,
f_min=0,
f_max=sr/2
)
mel_spec_torch = mel_transform(waveform)
mel_spec_db = torchaudio.transforms.AmplitudeToDB()(mel_spec_torch)
mfcc_transform = torchaudio.transforms.MFCC(
sample_rate=sr,
n_mfcc=13,
melkwargs={
'n_fft': 2048,
'hop_length': 512,
'n_mels': 40,
'f_min': 0,
'f_max': sr/2
}
)
mfcc_torch = mfcc_transform(waveform)
5.2 Augmentation de spectrogrammes
class SpecAugment:
"""Augmentation de spectrogrammes pour l'entraînement (SpecAugment paper)."""
def __init__(self, freq_mask=15, time_mask=30, num_freq_masks=2, num_time_masks=2):
self.freq_mask = freq_mask
self.time_mask = time_mask
self.num_freq_masks = num_freq_masks
self.num_time_masks = num_time_masks
def __call__(self, spec):
"""spec: (n_mels, n_frames) ou (batch, n_mels, n_frames)"""
if spec.ndim == 2:
spec = spec[np.newaxis, :, :]
augmented = spec.copy()
for _ in range(self.num_freq_masks):
f = np.random.randint(0, self.freq_mask)
f0 = np.random.randint(0, spec.shape[1] - f)
augmented[:, f0:f0+f, :] = 0
for _ in range(self.num_time_masks):
t = np.random.randint(0, self.time_mask)
t0 = np.random.randint(0, spec.shape[2] - t)
augmented[:, :, t0:t0+t] = 0
return augmented
class (nn.Module):
():
().__init__()
.freq_mask = freq_mask
.time_mask = time_mask
.num_freq_masks = num_freq_masks
.num_time_masks = num_time_masks
():
_ (.num_freq_masks):
f = torch.randint(, .freq_mask, (,)).item()
f0 = torch.randint(, x.size() - f, (,)).item()
x[:, f0:f0+f, :] =
_ (.num_time_masks):
t = torch.randint(, .time_mask, (,)).item()
t0 = torch.randint(, x.size() - t, (,)).item()
x[:, :, t0:t0+t] =
x
5.3 Normalisation des features
def cmvn(features, mask=None):
"""Normalisation moyenne-variance cepstrale."""
if features.ndim == 2:
mean = np.mean(features, axis=1, keepdims=True)
std = np.std(features, axis=1, keepdims=True)
return (features - mean) / (std + 1e-10)
else:
mean = np.mean(features, axis=2, keepdims=True)
std = np.std(features, axis=2, keepdims=True)
return (features - mean) / (std + 1e-10)
def pcmn(spec):
"""Soustraction de la moyenne par canal."""
return spec - np.mean(spec, axis=-1, keepdims=True)
def global_cmvn(features, global_mean, global_std):
"""Normalisation avec statistiques globales (calculées sur tout le dataset)."""
return (features - global_mean) / (global_std + 1e-10)
6. Outils CLI
6.1 songsee (spectrogrammes)
go install github.com/steipete/songsee/cmd/songsee@latest
songsee audio.wav --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux
songsee audio.wav --viz spectrogram --style magma -o spectro.png
6.2 audio-processing CLI
sox audio.wav -n spectrogram -o spectro.png
ffmpeg -i audio.wav -lavfi showspectrumpic=s=1920x1080 spectro.png
python3 -c "
import librosa, json, sys
audio, sr = librosa.load(sys.argv[1], sr=16000)
mfcc = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=13)
print(json.dumps({'mean': mfcc.mean(axis=1).tolist(), 'std': mfcc.std(axis=1).tolist()}))
" audio.wav
7. Pitfalls et solutions
| Problème | Cause | Solution |
|---|
| MFCC instables | Audio non normalisé | Normalisation du volume avant extraction |
| Bruit dans les hauts MFCC | Bruit HF | Limiter fmax à 8000Hz, filtrage |
| Artefacts Griffin-Lim | Phase non naturelle | Utiliser un vocoder neuronal (HiFi-GAN) |
| Spectrogramme flou | Fenêtre trop large | Réduire n_fft ou augmenter hop_length |
| Chroma non informatif | Timbre dominant | Utiliser CQT ou CENS au lieu de STFT |
| Overfitting ML | Features trop nombreuses | SpecAugment, CMVN, réduction dimensionnelle |
8. Références