Downloads · 30 days
0
pymaster/SingAlign
SingAlign is a audio-to-audio model from pymaster. Use it for the audio-to-audio task on the model card, and read the license before you ship it in a product. It is set up for singalign. The card lists the license as cc-by-nc-sa-4.0.
Weights for SingAlign, a unified forced-alignment toolkit for singing voice. The code lives in the GitHub repository; this repository holds only the checkpoints it downloads.
Downloads · 30 days
0
Access
Public
Updated Sep 16, 2026
Repo size
172 MB
Likes
0
Public
Click a slice to open those files.
.ckpt162 MB · 94%
From the Hugging Face model README
Weights for SingAlign, a unified forced-alignment toolkit for singing voice. The code lives in the GitHub repository; this repository holds only the checkpoints it downloads.
Files here are named exactly as configs/models/<name>.yaml declares them, so
the toolkit's resolver (modules/common/api/checkpoints.py) fetches them by
name and verifies the pinned SHA256 before it puts anything on disk.
| File | Family | Size | SHA256 |
|---|---|---|---|
hfa-mandarin-fullweak-v1.ckpt | HubertFA | 54.2 MB | d38f7d5839e2cd93e8dec5b77b6bd3ed28aa1318d5ed069c2cd81c899dd3c057 |
hfa-english-fullweak-v2.ckpt | HubertFA | 54.1 MB | 878de0899cb188c05537934a6f55bfd7aaabcbde1c411aba30d1e6f50a995712 |
hfa-english-fullweak-v1.ckpt | HubertFA (superseded by v2) | 54.1 MB | c1b2baa939a87b3ed832c97e18eba9fcb27214b39f50ff346c062601bd11c513 |
nll-hubert-ls960-v1.pt | breath head (NLL) for the English aligner | 9.9 MB | 03587e7a476aea7f137904e6034420d0a4b255d12949e85ec16b20077ae29ce8 |
hfa-mandarin-fullweak-v1A HubertFA alignment head over a frozen chinese-hubert-base front-end,
trained in-repo with train_hfa.py. It is SingAlign's default model.
configs/hfa/train_fullweak_v1.yaml, data
configs/hfa/binarize_fullweak.yaml.sofa-expanded-v1 profile (identity
against the toolkit's canonical Mandarin inventory).Scored on cloudtest-verified-v1: 8 recently released original songs
(90 clips, 1971 phones, 535.6 s) with hand-verified word + ph tiers, none
of whose song IDs or "artist – title" pairs appear in any training manifest.
Every system was fed the same gold phoneme sequence with G2P and breath
detection off, so this measures acoustic alignment only.
| System | VER20 ↓ | VER50 ↓ | mIoU ↑ | CBER ↓ |
|---|---|---|---|---|
hfa-mandarin-fullweak-v1 | 0.2265 | 0.0571 | 0.8030 | 0.1737 |
| HubertFA v0.0.7 (upstream ONNX) | 0.2075 | 0.0786 | 0.7734 | 0.2223 |
| SOFA ConvNeXt (in-repo) | 0.2519 | 0.0835 | 0.7747 | 0.2159 |
SOFA pretrained_mandarin_singing | 0.2575 | 0.0946 | 0.7501 | 0.2901 |
| STARS (Chinese) | 0.3379 | 0.1674 | 0.6072 | 0.6547 |
VER20/VER50 are vlabeler edit ratios at 20 ms / 50 ms boundary tolerance and
include SP; mIoU and CBER exclude it. Point estimates, no confidence
intervals.
Read this as an error profile, not a ranking. This model leads on VER50, mIoU and CBER but trails upstream v0.0.7 by ~9% on VER20: it makes fewer large errors and more small ones. If your criterion is a 20 ms tolerance, the upstream model is the better pick.
The torch path does not carry its own SSL front-end. The checkpoint records
hubert_config.model_path, and those encoder weights
(TencentGameMate/chinese-hubert-base,
~380 MB) must be present. python scripts/download_assets.py in the toolkit
fetches this checkpoint, the encoder and the G2pW model together.
git clone https://github.com/pymaster17/SingAlign && cd SingAlign
uv sync --extra pitch && source .venv/bin/activate
python scripts/download_assets.py # this checkpoint + encoder + G2pW
python infer_one.py -a audio.wav -t "中文歌词" --out_formats textgrid,json
from modules.api import HubertFATorchAligner
aligner = HubertFATorchAligner() # resolves to this checkpoint
results = aligner.align([{"id": "x", "audio_path": "a.wav", "text": "中文歌词"}])
hfa-english-fullweak-v2The English model: the same alignment head over a frozen
facebook/hubert-base-ls960 front-end, trained with the recipe of
hfa-english-fullweak-v1 below on data whose only change is where the clips
start.
configs/hfa/train_en_fullweak_v6.yaml (identical to v1's
except the run name), data configs/hfa/binarize_en_fullweak_v6.yaml.singmos >= 3.5 only, so the corpus's silence-cut sub segments (37 %) are
in the pool alongside the lyric-timed short ones.english-arpabet-v1, G2pEn); the
vocabulary file is byte-identical, so the two are drop-in for each other.v1 puts a spurious sub-millisecond SP at the head of about a third of clips
that start on the voice. The cause was traced to its weak-label pool: a
wer filter silently restricted it to lyric-timed segments, three quarters of
which start mid-voice, and CTC training on those teaches the shared phone
channels to read onset frames as "not yet a phone". Mixing in silence-cut
segments removes it: 32 % → 5.3 % of voice-initial GTSinger-en clips
(bench/tools/probe_leading_sp.py; docs/experiments/leading_sp_probe.md).
Same four held-out NUS-48E singers as v1 (201 clips, 0.53 h):
| System | CBER ↓ | VER20 ↓ | VER50 ↓ | mIoU ↑ |
|---|---|---|---|---|
hfa-english-fullweak-v2 | 0.1951 | 0.1327 | 0.0634 | 0.7823 |
hfa-english-fullweak-v1 | 0.1975 | 0.1318 | 0.0616 | 0.7821 |
| Same recipe, full label only (21.4 h) | 0.1940 | 0.1300 | 0.0617 | 0.7833 |
A tie with v1 on alignment quality; the caveat below about the weak label buying nothing measurable on clean studio audio still applies.
Unlike v1, this checkpoint passes the toolkit's batch-invariance gate:
bench/tools/validate_hfa_batch.py on the same 48 items, batch 8, measures a
maximum boundary shift of 0.002 ms against a 1.0 ms tolerance.
As v1: facebook/hubert-base-ls960
must be present, and the model is named explicitly:
from modules.api import HubertFATorchAligner
aligner = HubertFATorchAligner(ckpt="hfa-english-fullweak-v2", g2p="G2pEn")
results = aligner.align([{"id": "x", "audio_path": "a.wav", "text": "english lyrics"}])
hfa-english-fullweak-v1 (superseded)Kept because the CrawlSinger-en corpus's align_conf column was written by
it; use hfa-english-fullweak-v2 for new work. The same alignment head over a frozen facebook/hubert-base-ls960
front-end — same HuBERT-base recipe as the Mandarin model, so only the
encoder path and the phoneme inventory differ.
configs/hfa/train_en_fullweak.yaml, data
configs/hfa/binarize_en_fullweak.yaml.SP, english-arpabet-v1 profile.Scored on four held-out NUS-48E singers (201 clips, 0.53 h) — two from each mode of the corpus's F0 distribution, none of whom appears in training. The same suite selected the checkpoint, so read these as in-suite numbers.
| System | CBER ↓ | VER20 ↓ | VER50 ↓ | mIoU ↑ |
|---|---|---|---|---|
hfa-english-fullweak-v1 | 0.1975 | 0.1318 | 0.0616 | 0.7821 |
| Same recipe, full label only (21.4 h) | 0.1940 | 0.1300 | 0.0617 | 0.7833 |
The weak label bought nothing measurable here. The two models tie, and the
gap is smaller than this run's own validation-point scatter. It is released as
the default English model on the grounds that the Mandarin line measured weak
label buying robustness on rough recordings — but English has no rough-domain
held-out set to check that on, because the validation set is studio singing
while the weak label is crawled audio. If your material is clean, expect
nothing from the extra 1845 h. The full account is in
docs/experiments/hfa_en_full_weak_v1.md in the toolkit.
This checkpoint does not pass the toolkit's batch-invariance gate:
bench/tools/validate_hfa_batch.py measures a 10.5 ms maximum boundary
shift between batch-1 and batch-8 against a 1.0 ms tolerance. Scale: one
boundary of one clip in 48, between content phones (SH | IY) rather than on
an SP edge, so an adapter does not drop it.
Batching changes the floating-point reduction order in the linear layers, and
weak-label training leaves the logits flat enough at an acoustically ambiguous
boundary that the last-bit difference moves the Viterbi path. A deterministic
tie-break cannot fix it — the inputs genuinely differ. Set max_batch_size=1
if you need reproducibility. For comparison, the full-label-only model measures
0.003 ms on the same items.
facebook/hubert-base-ls960,
not the Mandarin encoder — the two are not interchangeable.
from modules.api import HubertFATorchAligner
aligner = HubertFATorchAligner(ckpt="hfa-english-fullweak-v1", g2p="G2pEn")
results = aligner.align([{"id": "x", "audio_path": "a.wav", "text": "english lyrics"}])
nll-hubert-ls960-v1A breath (non-lexical) head, not an aligner: a 2.45 M-parameter CVNT that
labels each 10 ms frame None / AP from the HuBERT units the aligner has
already computed, and whose breaths are spliced into the finished alignment as
AP without touching any other boundary. It rides on
hfa-english-fullweak-v2 (or v1) as ap_detector="nll" and is selected automatically
for any HubertFA aligner whose front-end is hubert-base-ls960.
chinese-hubert-base units; run over hubert-base-ls960 units
(same 768 width, unrelated latent space) it recalls 0.001 of the breaths
in held-out English singing -- nothing. The toolkit now refuses that pairing.configs/nll/train_v2.yaml, data configs/nll/binarize_v2.yaml;
class-weighted CE + focal + dice, 15000 steps, step 7500 selected on
frame-F1 over the annotation-consistent held-out groups.None / AP. No tail-breath (EP) class.Frame-F1 at threshold 0.5, scored only inside the gold non-content regions (where a breath could be), on songs held out by title:
| Held-out set | This head | Upstream head on its own front-end |
|---|---|---|
| GTSinger Chinese (2 singers, 1.7 h) | 0.885 / 0.872 | 0.844 / 0.790 |
| GTSinger English, consistent singers (1.3 h) | 0.782 / 0.750 | 0.746 / 0.712 |
| M4Singer held-out songs (3.0 h) | 0.893 | 0.826 |
| Opencpop, all 3756 clips (5.2 h, never trained on) | 0.815 | 0.931 |
The Opencpop gap is a known limitation: this head still over-fires on
Opencpop's silent gaps (precision 0.69 at recall 0.99). Full account, including
the per-singer annotation probe:
docs/experiments/nll_hubert_ls960_v1.md in the toolkit.
from modules.api import HubertFATorchAligner
aligner = HubertFATorchAligner(ckpt="hfa-english-fullweak-v2", g2p="G2pEn",
ap_detector="nll") # resolves to this head
Pairing it with a chinese-hubert-base aligner raises; the Mandarin line keeps
the upstream head.
All checkpoints are derived from corpora with their own terms. Attribution is required by several of them; the ShareAlike terms are why this repository is CC-BY-NC-SA-4.0 rather than CC-BY-NC-4.0.
| Corpus | Used by | Terms |
|---|---|---|
| GTSinger | all three | CC BY-NC-SA 4.0 — attribution, non-commercial, ShareAlike |
| M4Singer | Mandarin, breath head | CC BY-NC-SA 4.0; cite Zhang et al., M4Singer, NeurIPS 2022 |
| Opencpop | Mandarin | research corpus; cite its paper |
| Project-AIdol | English | CC BY-SA 4.0. Created by Lotte V (@lottev1991). The dataset asks that it not be used with voice changers (RVC and similar), and that models featuring the voice not be publicly released without prior permission — this is an aligner, which predicts phone boundaries and cannot reproduce a voice. |
| ACV-001 | English | supplied as supplementary data; check the source for its current terms |
| NUS-48E | English | shared for research purposes only; cite Duan, Fang, Li, Sim and Wang, The NUS Sung and Spoken Lyrics Corpus, APSIPA ASC 2013 |
| CrawlSinger (zh / en) | both | scraped singing; the underlying recordings are third-party copyrighted works |
No audio from any of these corpora is redistributed here — only trained weights.
confidence does not transfer across models. SOFA scores land in
~[0.79, 1] and HubertFA in ~[0.67, 0.95], and the score is a top-2 pairwise
margin. Any threshold inherited from another model has to be re-calibrated.