| """Audio feature extraction for Music2Pose inference.""" |
|
|
| import numpy as np |
| import librosa |
|
|
| AUDIO_SR = 32_000 |
| POSE_FPS = 25 |
| HOP_SAMPLES = AUDIO_SR // POSE_FPS |
| N_MELS = 80 |
| CONTEXT_LEN = 60 |
|
|
|
|
| def audio_to_features(waveform: np.ndarray) -> np.ndarray: |
| """Convert mono waveform (n_samples,) → (T, 82) mel + onset + beat features.""" |
| y = waveform.astype(np.float32) |
|
|
| mel = librosa.feature.melspectrogram( |
| y=y, sr=AUDIO_SR, n_mels=N_MELS, hop_length=HOP_SAMPLES |
| ) |
| mel_db = librosa.power_to_db(mel, ref=np.max) |
|
|
| onset = librosa.onset.onset_strength( |
| y=y, sr=AUDIO_SR, hop_length=HOP_SAMPLES |
| ) |
| _, beats = librosa.beat.beat_track( |
| y=y, sr=AUDIO_SR, hop_length=HOP_SAMPLES |
| ) |
|
|
| T = min(mel_db.shape[1], len(onset)) |
| mel_db = mel_db[:, :T] |
| onset = onset[:T] |
|
|
| beat_sig = np.zeros(T, dtype=np.float32) |
| beat_sig[beats[beats < T].astype(int)] = 1.0 |
|
|
| return np.concatenate( |
| [mel_db.T, onset[:, None], beat_sig[:, None]], axis=-1 |
| ).astype(np.float32) |
|
|