| |
| """Feasibility spike: can we detect the tempo curve + onsets on a real DI track? |
| |
| Runs librosa onset detection + frame-wise tempo on the DI WAV and compares |
| against the notated tempo from the MIDI time map (if given). |
| """ |
| import argparse |
| import sys |
|
|
| import librosa |
| import numpy as np |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("wav", help="DI track WAV path") |
| ap.add_argument("--sr", type=int, default=22050, help="resample target") |
| args = ap.parse_args() |
|
|
| print(f"Loading {args.wav} ...") |
| y, sr = librosa.load(args.wav, sr=args.sr, mono=True) |
| dur = len(y) / sr |
| print(f" duration {dur:.1f}s, sr {sr}, samples {len(y)}") |
|
|
| |
| print("\n[onset] detecting onset strength ...") |
| onset_env = librosa.onset.onset_strength(y=y, sr=sr) |
| onsets = librosa.onset.onset_detect(onset_envelope=onset_env, sr=sr, |
| units="time", backtrack=True) |
| print(f" {len(onsets)} onsets") |
| |
| if len(onsets) > 1: |
| inter = np.diff(onsets) |
| inter = inter[inter > 0.08] |
| if len(inter): |
| rate = 60.0 / np.median(inter) |
| print(f" median inter-onset {np.median(inter):.3f}s -> ~{rate:.1f} BPM") |
|
|
| |
| print("\n[tempo] frame-wise tempo curve ...") |
| tempo_out = librosa.feature.tempo( |
| onset_envelope=onset_env, sr=sr, aggregate=None, |
| ) |
| |
| if np.isscalar(tempo_out) or (np.ndim(tempo_out) == 0): |
| tempo_curve = np.full(onset_env.shape[-1], float(tempo_out)) |
| else: |
| tempo_curve = np.asarray(tempo_out).reshape(-1) |
| hop = librosa.get_hop_length() if hasattr(librosa, "get_hop_length") else 512 |
| frame_dur = hop / sr |
| n = len(tempo_curve) |
| |
| win = max(1, int(4.0 / frame_dur)) |
| print(f" {n} frames, frame {frame_dur:.2f}s") |
| t = 0.0 |
| i = 0 |
| print("\n time(s) ~BPM(median 4s window)") |
| seen = [] |
| while i < n: |
| w = tempo_curve[i:i + win] |
| med = float(np.median(w[np.isfinite(w)])) if np.any(np.isfinite(w)) else float("nan") |
| t = i * frame_dur |
| seen.append((t, med)) |
| print(f" {t:7.1f} {med:6.1f}") |
| i += win |
| |
| uniq = sorted(set(round(m) for _, m in seen if np.isfinite(m))) |
| print("\n distinct ~tempo values:", uniq) |
|
|
| |
| print("\n[beat] beat_track ...") |
| tempo_est, beats = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr) |
| if np.ndim(tempo_est): |
| tempo_est = float(np.median(np.asarray(tempo_est))) |
| beat_times = librosa.frames_to_time(beats, sr=sr) |
| print(f" est BPM {float(tempo_est):.1f}, {len(beat_times)} beats") |
| if len(beat_times) > 1: |
| ii = np.diff(beat_times); ii = ii[ii > 0] |
| print(f" median beat interval {np.median(ii):.3f}s -> ~{60/np.median(ii):.1f} BPM") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|