Speed up analysis: drop BPM, vectorise loudness/true-peak, share short-term

Profiled hot spots on a 4-min track and cut the worst offenders:

- Remove BPM: librosa.beat.beat_track ran on every load (~3.7s) for a number no
  better than tapping by hand. Dropped from AudioFile + the metadata panel.
- LUFS short-term: replace 474 per-window pyloudnorm.integrated_loudness calls
  with one K-weighting pass (reusing pyloudnorm's own filter coefficients) + a
  vectorised sliding mean-square. This is true *ungated* EBU R128 short-term
  (the old loop wrongly gated each 3s window). Integrated + LRA still use
  pyloudnorm's gated calls. ~3.8s -> ~1.9s.
- PSR: reuse LUFS's short-term series (memoised on the AudioFile) + vectorised
  sample-peak. ~3.0s -> ~0.2s.
- True Peak: oversample the whole signal once, then an O(N) running max over
  windows instead of per-window resample_poly. Bit-identical to the old loop
  (max|diff| 0.0000 dB). ~2.1s -> ~1.1s.
- Crest Factor: peaks via the same O(N) running max (last per-window loop gone).

lufs+psr+true_peak: ~9.2s -> ~3.2s, plus ~3.7s of BPM removed from every load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2026-06-14 01:36:08 +09:00
parent 3707917d9e
commit d7782bb9d9
4 changed files with 120 additions and 82 deletions
+2 -7
View File
@@ -34,13 +34,8 @@ class AudioFile:
self.y_mono = librosa.to_mono(self.y)
self.max_amplitude = np.max(np.abs(self.y_mono))
self.avg_amplitude = np.mean(np.abs(self.y_mono))
self.bpm, _ = librosa.beat.beat_track(y=self.y_mono, sr=self.sr)
def get_bpm(self):
# librosa.beat.beat_track returns numpy array - extract scalar value
if isinstance(self.bpm, np.ndarray):
return float(self.bpm[0]) if len(self.bpm) > 0 else 0.0
return float(self.bpm)
# BPM intentionally not computed: librosa.beat.beat_track cost ~3.7s on a
# 4-min track for a number that's no better than tapping it by hand.
def get_energy_levels_over_time(self, window=10, hop=2):
"""Compute rolling RMS power.