Automatic Lyrics Transcription (ALT): 2025–2026 Engineering Research Brief

Scope: practical best-in-class setups for transcribing lyrics from full music mixes, with published benchmark numbers and a concrete Apple Silicon recommendation.

Headline findings (TL;DR): - Naive Whisper-large on a full mix gets ~28–36% WER on the standard Jam-ALT benchmark; the best published open pipeline (source-separation-assisted segmentation + Whisper) gets ~20.4%; the best published commercial system (AudioShake) gets ~16% (Jam-ALT/ICME 2025, arXiv 2506.15514). - Source separation is not a free win for Whisper: feeding separated vocals directly can degrade results unless the separator is high quality; the most robust use of separation is as a vocal-activity detector for segmentation, transcribing segments of the original mix. - Whisper large-v2 often beats large-v3 on lyrics, and large-v3-turbo is measurably worse on music. Use condition_on_previous_text=False to kill repetition loops.


1. Why lyrics transcription is harder than speech ASR

Singing breaks most assumptions baked into speech-trained ASR models. The survey “More than words: Advancements and Challenges in Speech Recognition for Singing” (arXiv 2403.09298) and the foundational “Automatic Lyrics Alignment and Transcription in Polyphonic Music: Does Background Music Help?” (arXiv 1909.10200) identify the core problems:

2. Vocal source separation as preprocessing — does it help?

Separator quality landscape (MUSDB18-HQ vocals SDR)

Does separating before ASR reduce WER? The evidence is nuanced.

Engineering takeaway: with stock Whisper, (a) always use separation to segment (VAD); (b) only feed separated vocals to Whisper if your separator is BS-RoFormer-class (≥~9–12 dB vocals SDR); (c) consider transcribing both mix and separated vocals and reconciling — that’s effectively what commercial systems and LyricWhiz-style ensembles exploit.

3. ASR models for singing voice

Whisper family behavior on sung vocals

Singing-specific systems

4. Known best practices (decode-time)

  1. condition_on_previous_text=False — the single most important anti-repetition-loop setting for music; instrumental gaps otherwise poison subsequent windows (whisper discussion #679, Memo AI writeup). Cost: loses cross-segment context (slightly worse consistency).
  2. Language hint — always pass language=; Jam-ALT shows +lang improves Whisper and avoids per-segment misdetection on sung vowels (arXiv 2408.06370).
  3. VAD before Whisper — don’t let Whisper see instrumental-only audio. Either faster-whisper’s built-in Silero vad_filter=True (faster-whisper) or, better for music, energy/RMS-VAD computed on the separated vocal stem, then cut the original mix at those boundaries — this is the published SOTA open recipe (20.35% Jam-ALT WER, arXiv 2506.15514). Note Silero is speech-trained and can be unreliable on singing; vocal-stem RMS sidesteps that.
  4. Temperature fallback — keep the default ladder (0.0, 0.2, …, 1.0) with compression_ratio_threshold=2.4 and logprob_threshold=-1.0; these gate the retry that escapes degenerate beams. Greedy/beam at T=0 plus fallback beats fixed nonzero temperature.
  5. no_speech_threshold — lower it (e.g., 0.4–0.6) and drop high no-speech-probability segments; LyricWhiz dropped predictions with no-speech prob > 0.9 (arXiv 2306.17103v4).
  6. initial_prompt — LyricWhiz’s "lyrics:" prompt (localized: “paroles”, “Liedtext”) nudges the decoder toward lyric register; you can also seed artist/title or known chorus lines. Beware: prompts can also seed hallucinations on silent stretches.
  7. Chunking — prefer segment boundaries at vocal-activity gaps (from VAD above) padded to ≤30 s windows; for short-form scoring, the concatenation approach in arXiv 2506.15514 reduced WER consistently. hallucination_silence_threshold (openai/mlx implementations) helps skip long silences when using word timestamps.
  8. Multi-run + LLM rerank (optional, biggest accuracy lever after separation): run 3–5 decodes (vary temperature/seed), have an LLM merge — LyricWhiz pattern, worth ~5–10 WER points on hard genres (arXiv 2306.17103).

5. Evaluation methodology

System WER Source
Whisper large-v2 (+lang) 27.9% 2408.06370
Whisper large-v3 (+lang) 32.6% 2408.06370
Whisper large-v2, sep-assisted RMS-VAD long-form 20.35% (open SOTA) 2506.15514
LyricWhiz (Whisper+GPT-4) 24.6% 2408.06370
OWSM v3.1 69.3% 2408.06370
AudioShake v3 (commercial) 16.1% 2408.06370
Whisper large-v2 on clean vocal stems (MUSDB-ALT) ~14.2% 2506.15514

Per-language (Whisper v2 +lang): EN 39.7, ES 21.9, DE 19.9, FR 27.1 — English is paradoxically hardest in this set (genre mix). Expect 20–25% WER from a good open pipeline, 35–50%+ on rock/metal/heavy mixes, near-15% only with clean stems or commercial systems.

6. Concrete recommendation: Mac (Apple Silicon) pipeline

Primary recommendation (best published-evidence open pipeline):

  1. Separate vocals with a RoFormer-class model via python-audio-separator (pip install "audio-separator[cpu]" — uses CoreML execution provider on macOS; e.g. model_bs_roformer_ep_317_sdr_12.97.ckpt), or the MLX-native fork mlx-audio-separator. Fallback: demucs -n htdemucs_ft (demucs).

  2. Segment using the separated vocal as a VAD: RMS-energy gate on the vocal stem → sung-region boundaries, merge gaps < ~1 s, pad ~0.3 s, cap windows at 30 s (recipe per arXiv 2506.15514).

  3. Transcribe with mlx-whisper, model mlx-community/whisper-large-v2-mlx (benchmark v3 too; v2 is the safer lyrics default), per segment:

    mlx_whisper.transcribe(
        seg_audio,
        path_or_hf_repo="mlx-community/whisper-large-v2-mlx",
        language="en",                      # always hint
        condition_on_previous_text=False,   # kills repetition loops
        temperature=(0.0, 0.2, 0.4, 0.6, 0.8, 1.0),  # fallback ladder
        compression_ratio_threshold=2.4,
        logprob_threshold=-1.0,
        no_speech_threshold=0.5,
        initial_prompt="lyrics:",           # LyricWhiz trick; A/B it
    )
  4. Feed segments from the original mix by default; if your separator is BS-RoFormer-class, also transcribe the separated-vocal segments and reconcile the two passes with an LLM (LyricWhiz pattern: present both candidates, ask for the most plausible lyric line). Expected: ~20% WER territory on Jam-ALT-like material; the LLM merge is your path below that.

Runner-up configurations to benchmark against: - faster-whisper large-v2/v3 with vad_filter=True, beam_size=5, condition_on_previous_text=False on the mix — simplest credible baseline (faster-whisper). - whisper.cpp large-v3 (not turbo — turbo demonstrably drops vocalizations on music, whisper.cpp #3074) if you want a zero-Python binary; expect ~2x slower than MLX on M-series (benchmark). - Gemini 2.5/3 Pro full-song audio prompt — unbenchmarked on Jam-ALT but cheap to test and strong anecdotally; include in your bake-off. - AudioShake API if budget allows — only system with published ~16% Jam-ALT WER plus correct line breaks/casing (AudioShake).

Evaluate with alt-eval on the jam-alt dataset (and MUSDB-ALT if you care about long-form), so your numbers are comparable to the literature; don’t hand-roll WER normalization.

Known residual failure modes no pipeline fixes today: backing vocals/harmonies (systematically deleted), non-lexical vocables, heavily distorted genres (rock/metal), and choruses with stacked vocals — budget for human review there.