Skip to content

Add --tags-only and a batch summary, speed up the analysis, and fix the chroma sample rate - #4

Merged
Xehanort88 merged 5 commits into
masterfrom
analysis-performance
Sep 27, 2026
Merged

Xehanort88 merged 5 commits into
masterfrom
analysis-performance

Conversation

@Xehanort88

@Xehanort88 Xehanort88 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • --tags-only: uses only the loop points stored in the files' metadata tags, skipping the loop analysis; files without valid loop tags are skipped instead of silently falling back to detected loop points. Available for all commands.
  • Batch summary: batch runs end with a summary of the processed files (with loop points from their tags, or detected ones), and of the skipped and failed files grouped by reason; non-audio files are counted by extension. Skipped files are no longer logged as errors during the run (they are listed in the summary; -v still logs them).
  • Faster analysis with half the memory, with unchanged results: the candidate loop search is ~60x faster, and the peak memory of the analysis is about halved.
  • Chroma pitch classes use the audio's sample rate: chroma_stft assumed 22050 Hz, which shifted the pitch classes of 48 kHz audio by ~1.5 semitones. This is a correctness fix: it changes the best detected loop of some tracks, with no measurable effect on the loop quality overall.

Analysis

The performance profiling, the verification that the optimizations give bit-identical results, and the evaluation of the sample rate fix on 151 tracks were done by Claude (Claude Code, Anthropic's Opus 5.5 model). There is no way I could have done this analysis myself to the same level of precision.

Performance (results unchanged)

The full loop pair lists (positions, order and scores) are bit-identical before and after, in normal, --approx-loop-position and --brute-force modes.

Track Analysis time Peak memory
3.8 min, 48 kHz 2.7 s → 2.4 s 1.39 GB → 0.78 GB
7.4 min, 44.1 kHz 4.3 s → 3.0 s 2.31 GB → 1.20 GB
19.4 min, 48 kHz 14.1 s → 7.8 s 6.20 GB → 2.98 GB
  • _find_candidate_pairs compares every pair of beats; it now uses scalar loops over the beats' chroma and loudness instead of allocating temporary arrays for each comparison (4.3 s → 0.07 s on the 19 min track; brute force benefits the most).
  • Only the loudest bin of each frame of the dB spectrogram was used, so only those are kept (854 MB → 0.4 MB), and the spectrograms are released as soon as they are no longer needed.
  • The biggest memory spike (~3 GB) was librosa's pitch tracking for the chroma's tuning estimate; it is now computed over chunks of frames, with the same result.

Chroma sample rate

The old and new best loops were compared on 151 tracks, by how well the audio after the loop start matches the audio after the loop end (1.0 = identical). The fix corrects the analysis, but makes no measurable difference to the loop quality overall: one track is clearly better, one is worse, and the rest are unchanged or differ by noise.

Tracks Best loop changed Result
35 game tracks with loop tags (exact repeats, waveform similarity) 15 1 much better (0.542 → 0.992), others unchanged or within ±0.011
116 other tracks (musical log-mel similarity) 82 mean 0.898 → 0.900; 11 better, 11 worse by > 0.05 (Wilcoxon p = 0.45)
… of which 12 contain an exact repeat (waveform similarity) — 10 equally good; 1 slightly better; 1 worse (0.917 → 0.748: same loop length, shifted by 7.8 s)

48 further tracks (mostly non-looping film scores) had no loop found with either version.

Beat tracking has the same default sample rate, which only scales the bpm used for the length of the scoring window; passing the real sample rate there gave no better loops (and one clearly worse), so it is documented in the code and kept as-is.

Test plan

  • uv run pytest (145 tests, including new ones comparing the optimized computations with the original ones, and checking that an A4 tone is analyzed as A at 22.05, 44.1 and 48 kHz)
  • --tags-only batch runs on real folders (214 and 41 files)
  • Old vs new chroma compared on 151 real tracks (see above)

…ch summary

--tags-only uses only the loop points stored in the file's metadata tags, without analyzing the audio; files without valid loop tags are skipped. Batch runs now end with a summary of the processed files (with loop points from their tags or detected ones), and of the skipped and failed files grouped by reason.
The candidate search compares every pair of beats; it now does so with scalar loops over the beats' chroma and loudness instead of allocating temporary arrays for each comparison (about 60x faster, and much faster brute force). Only the loudest bin of each frame of the dB spectrogram was used, so only those are kept, the spectrograms are released as soon as they are no longer needed, and the tuning estimate of the chroma is computed over chunks of frames. The results are unchanged (bit-identical loop points and scores).
chroma_stft was not given the sample rate, so it assumed librosa's default of 22050 Hz: pitch classes were shifted by ~1.5 semitones for 48 kHz audio, and the chroma filter bank weighted the wrong octaves for 44.1 kHz audio.

This is a correctness fix: it changes the best detected loop of some tracks, but compared on 151 real tracks it makes no measurable difference to the loop quality overall (one track clearly better, one worse, the rest unchanged or within noise).

Beat tracking has the same default, which only scales the bpm used for the length of the scoring window; passing the real rate there gave no better loops, so it is documented and kept.
Adds an Unreleased section listing the changes since 3.6.0, regenerates the --help images (with --tags-only), and adds the trim command to CLI_README.md.
@Xehanort88
Xehanort88 merged commit 7f4a08a into master Sep 27, 2026
8 checks passed
@Xehanort88
Xehanort88 deleted the analysis-performance branch September 27, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant