song

Lyrics, timed to the word.

A track and a text file in. Synced lyrics and a finished video out. Nothing leaves your machine.

.lrc · word .lrc · .srt · .vtt · .ass · .mp4

One command. 1080p, 60 fps. Every pixel behind the words came out of the audio — no footage, no template, nothing to supply.

The premise

You already know the words.

So this is forced alignment, not transcription. A chorus that repeats four times resolves by position, for free.

The timeline zoomed to one line, word cells along the bottom with dividers running the full height of the waveform.
Word boundaries are cut against the audio itself — a divider that misses the attack is visible against the thing it is cutting.
94.5mean line score
33/33lines aligned
160msmedian disagreement
99%vocal coverage
Three models, cross-examining each other

Demucs isolates the vocal — aligning against the stem rather than the mix is the single biggest accuracy win on dense productions. wav2vec2 CTC anchors the structure in one global pass, so a 4-bar instrumental is absorbed as blanks instead of sliding the rest of the song. Whisper then refines inside each ~20-second section, where it is both accurate and precise.

All three are told the lyrics, so all three can be confidently wrong in the same place. A fourth pass is told nothing and reports what it hears. It is the strongest single signal in the merge — and it is what finds the chorus repeat somebody forgot to type.

Run against a deliberately degraded alignment the mean score falls from 94.5 to 69.9 and lines needing review go from 2 to 11. It discriminates.

Where models disagree

You settle it by ear.

Two aligners agreed on 137 of 177 words — you never see those. The rest play you both candidates. If neither is right, drag a third onto the waveform.

The Check timings card: a waveform strip with blue, green and violet markers, and three options — Now, Suggested and Adjusted — each with its own play button.
What is deliberately not automated

Snapping every word start to its nearest onset. It looks like free accuracy, but on this track 41% of word starts have more than one onset within ±150 ms — snapping would be a coin flip wearing a lab coat. Where two models disagree and both readings are plausible, no heuristic settles it honestly, so an ear does.

The whole track

One waveform, every line.

Colour means act here and nothing else.

The song app: a waveform with every lyric line bracketed, and the lyrics listed below with per-line scores.

Two commands.

Five minutes to align a five-minute track. Twelve more to render it at 1080p60.

./setup.sh
./.venv/bin/python -m song
Every command, and what lands on disk
python -m song                          # open the app
python -m song track.wav lyrics.txt     # align, then open it
python -m song audit workdir/my-track   # repair + list what needs an ear
python -m song score workdir/my-track   # re-run the benchmark on your edits
python -m song video workdir/my-track   # render karaoke.mp4

Everything lands in workdir/<track>/: lyrics.lrc, lyrics.word.lrc, lyrics.srt, .vtt, and project.json — word timings, per-line scores and the scorecard. song video adds lyrics.ass and karaoke.mp4.

Lyrics input is plain text: one line per lyric line, a blank line between sections, a header naming each. Demucs on Apple MPS is broken under torch 2.5, so it is CPU throughout.