Charts could fill an instrumental break with a run of ~ notes when a sung word's alignment stretched into the break.
Cause
The separated vocal stem isn't truly silent during instrumental passages: faint bleed remains, at around -60 dB A-weighted. crepe still finds a pitch in it, and 14–44% of those frames had periodicity of at least 0.5, so they passed the notes stage's voicing mask. When the forced aligner stretched a word's end across such a gap, every one of those frames became a note.
Fix
track_pitch now zeroes the confidence of frames more than 25 dB below the song's 90th-percentile A-weighted loudness (torchcrepe.loudness.a_weighted, the same measure torchcrepe.threshold.Silence uses). The threshold is relative so that it holds however loud the song was mastered. The README's fixed -60 dB only caught 18% of the bleed frames on the test song, because the bleed sat right at that level.
Existing songs can be fixed by rebuilding with Restart from set to pitch.
Testing
Unit tests check that bleed is muted, quiet singing is kept, and the threshold follows the song's own level.
I ran the real pitch and notes stages twice on CPU on a song from the user's library, once with the gate and once without, against the same alignment. Notes inside an 8s instrumental gap went from 10 to 1, and the total from 691 to 679. Elsewhere the counts changed by at most one note per 10s window. Only 0.3% of voiced frames inside aligned words were muted.
Full suite passes, except test_cli.py::test_version (needs the package installed) and the intermittent test_web.py failures that also happen on main.
**Summary generated by LLM agent**
Charts could fill an instrumental break with a run of `~` notes when a sung word's alignment stretched into the break.
## Cause
The separated vocal stem isn't truly silent during instrumental passages: faint bleed remains, at around -60 dB A-weighted. crepe still finds a pitch in it, and 14–44% of those frames had periodicity of at least 0.5, so they passed the notes stage's voicing mask. When the forced aligner stretched a word's end across such a gap, every one of those frames became a note.
## Fix
`track_pitch` now zeroes the confidence of frames more than 25 dB below the song's 90th-percentile A-weighted loudness (`torchcrepe.loudness.a_weighted`, the same measure `torchcrepe.threshold.Silence` uses). The threshold is relative so that it holds however loud the song was mastered. The README's fixed -60 dB only caught 18% of the bleed frames on the test song, because the bleed sat right at that level.
Existing songs can be fixed by rebuilding with **Restart from** set to `pitch`.
## Testing
- Unit tests check that bleed is muted, quiet singing is kept, and the threshold follows the song's own level.
- I ran the real pitch and notes stages twice on CPU on a song from the user's library, once with the gate and once without, against the same alignment. Notes inside an 8s instrumental gap went from 10 to 1, and the total from 691 to 679. Elsewhere the counts changed by at most one note per 10s window. Only 0.3% of voiced frames inside aligned words were muted.
- Full suite passes, except `test_cli.py::test_version` (needs the package installed) and the intermittent `test_web.py` failures that also happen on `main`.
Separated stems keep faint bleed during instrumental passages, and crepe
finds a pitch in it with enough confidence to pass the voicing mask. A word
whose alignment ran into such a gap was then filled with notes for the
whole gap. Zero the confidence of frames more than 25 dB below the song's
90th-percentile A-weighted loudness, relative so it holds at any mastering
level.
branderson
approved these changes 2026-10-09 18:15:17 -07:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Charts could fill an instrumental break with a run of
~notes when a sung word's alignment stretched into the break.Cause
The separated vocal stem isn't truly silent during instrumental passages: faint bleed remains, at around -60 dB A-weighted. crepe still finds a pitch in it, and 14–44% of those frames had periodicity of at least 0.5, so they passed the notes stage's voicing mask. When the forced aligner stretched a word's end across such a gap, every one of those frames became a note.
Fix
track_pitchnow zeroes the confidence of frames more than 25 dB below the song's 90th-percentile A-weighted loudness (torchcrepe.loudness.a_weighted, the same measuretorchcrepe.threshold.Silenceuses). The threshold is relative so that it holds however loud the song was mastered. The README's fixed -60 dB only caught 18% of the bleed frames on the test song, because the bleed sat right at that level.Existing songs can be fixed by rebuilding with Restart from set to
pitch.Testing
test_cli.py::test_version(needs the package installed) and the intermittenttest_web.pyfailures that also happen onmain.