perf(pipeline): single-pass streamed peaks + presence - #306
Merged
Conversation
app/pipeline/audio_stats.py: new scan_stem() does one streamed pass over a stem WAV via sf.blocks() -- [min, max] per bucket (waveform peaks) and RMS (stem presence), both from the same blocks. Constant memory: a block is a few MB even for a 20-minute stereo stem, vs. sf.read()'s full in-memory load (~420 MB for the same file, done for up to 8 files back-to-back right after Demucs has already stressed memory -- a plausible contributor to OOM failures on memory-constrained machines). collect.compute_stem_peaks now delegates to scan_stem and returns each stem's RMS from the same pass; peaks.json's format and bucketing are unchanged (floor-division chunking, matching the old implementation bucket-for-bucket -- verified by a golden test comparing against the old sf.read()-then-chunk reference). runner._run_common now derives stem_presence from that RMS map (moved out of analyze.compute_stem_presence, which is deleted along with its separate ffmpeg-downmix decode of every stem) instead of decoding each stem twice. Known, accepted delta: presence RMS is now measured over the full stem at full sample rate, vs. the old ffmpeg-downmixed mono decode capped at the first 180s. On a real 220s track this shifted some quiet-stem presence values by up to ~6 points (piano 1->6, other 12->19) -- larger than initially estimated, but a strict accuracy improvement (whole track, not a 3-minute window), not a regression. Closes #286 Closes #287
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR-3B of Phase 3 (Performance).
app/pipeline/audio_stats.py::scan_stem()does one streamed pass over a stem WAV viasf.blocks(), producing both the waveform peaks ([min, max]per bucket) and RMS (for stem presence) from the same blocks. Constant memory — a block is a few MB even for a 20-minute stereo stem, vs.sf.read()'s full in-memory load (~420 MB for the same file, done for up to 8 stems back-to-back right after Demucs has already stressed memory — a plausible contributor to OOM failures on memory-constrained machines like the reported 8 GB Mac Mini).collect.compute_stem_peaksnow delegates toscan_stemand additionally returns each stem's RMS.peaks.json's format and bucketing are unchanged — floor-division chunking, matching the old implementation bucket-for-bucket (verified by a golden test comparing against the oldsf.read()-then-chunk reference on a real multi-tone signal).runner._run_commonnow derivesstem_presencefrom that RMS map instead of a second, separate decode.analyze.compute_stem_presence(which spawned its own ffmpeg downmix per stem) is deleted.Closes #286
Closes #287
Known, accepted delta
Presence RMS is now measured over the full stem at full sample rate, vs. the old ffmpeg-downmixed mono decode capped at the first 180s. Sanity-checked against a real 220s track from a local job folder: this shifted some quiet-stem presence values by up to ~6 points (piano 1→6, other 12→19) — larger than the original estimate of "≲1-2 points," but a strict accuracy improvement (whole track vs. a 3-minute window), not a regression. Loud stems (bass, drums, vocals) moved by 2-6 points too, same direction.
Test plan
tests/test_pipeline_collect.py: RMS matches a full-loadnp.sqrt(mean(x**2))reference within tolerance; a stem missing from disk is excluded from the returned RMS map (same as peaks); golden test — streamed peaks match the oldsf.read()-then-chunk implementation bucket-for-bucket on a multi-tone signal.tests/test_pipeline_runner.pyfor_presence_from_rms: normalizes to the loudest stem, empty input, all-silent input.compute_stem_peaksagainst a real 8-stem job folder (220s track) — correct RMS values,peaks.jsonwritten, ~0.8s for all 8 stems.194 passed, 12 skippedruff check/ruff format --checkclean