Skip to content

feat: --dump-segments, so a lyrics edit doesn't re-run the ASR - #29

Merged
ijuinryukichi merged 1 commit into
mainfrom
feat/dump-segments
Sep 20, 2026
Merged

ijuinryukichi merged 1 commit into
mainfrom
feat/dump-segments

Conversation

@ijuinryukichi

Copy link
Copy Markdown
Owner

--segments could read a segments file, but nothing could write one — -f json
emits aligned lines, and Segment had from_dict without its mirror. The cheap
path existed and was unreachable from the CLI.

What this adds

--dump-segments FILE writes the transcription in exactly the shape --segments
reads back, and Segment.to_dict / Word.to_dict to go with it.

lyric-align song.wav lyrics.txt --separate --dump-segments segs.json -o out.lrc
# fix a typo in lyrics.txt
lyric-align --segments segs.json lyrics.txt -o out.lrc      # seconds, not minutes

The write lands before alignment, not after: the expensive half is done, the
cheap half is the one you re-run, and a run that dies in the cheap half must not
cost the audio work. That is the case #28 came from. When --segments was the
input it no-ops with a note rather than copying the input under a second name.

Measured

Real end-to-end on the public-domain demo (tiny model, no Demucs):

wall clock
transcribe + align + dump 7.02 s
re-run off the dump 0.04 s

Output byte-identical between the two. With medium and --separate the first
number is minutes.

The file is one segment per line instead of a plain indent=, which puts every
word timing on three lines of its own — 443 lines vs 8 for a 6-segment song.
Floats are left exactly as the ASR gave them; rounding would make the documented
from_dict(to_dict(x)) == x a lie for a cosmetic gain.

Tests

Six new tests (3 model, 3+1 CLI). Five mutations run against them, each caught by
the test that claims to guard it:

mutation caught by
to_dict drops word timings round-trip, word-timing, CLI word-timing (3)
dump moved after alignment the crash-survival test (+1)
no-op guard removed the --segments no-op test
hand-written brackets broken every test that parses the file (4)
words re-expanded with indent= the one-line-per-segment test

Full suite: 67 passed on 3.9 and 3.14.

Version bumped to 0.4.0, matching how #6 handled a feature. Publishing is a
separate decision.

Closes #28

🤖 Generated with Claude Code

`--segments` could read a segments file, but nothing could write one: `-f json`
emits aligned lines, and `Segment` had `from_dict` without its mirror. So the
cheap path existed and was unreachable from the CLI.

`--dump-segments FILE` writes the transcription in exactly the shape
`--segments` reads back. It lands *before* alignment, so a run that dies in the
cheap half still leaves the expensive half on disk — that is the case the
request came from.

Measured on the public-domain demo (tiny model, no Demucs): 7.02s for the
transcribing run, 0.04s re-running off the dump, byte-identical output. With
`medium` and `--separate` the first number is minutes.

The file is written one segment per line rather than with a plain `indent=`,
which would put every word timing on three lines of its own — 443 lines instead
of 8 for a 6-segment song.

Five mutations were run against the new tests (dropping word timings, moving
the dump after alignment, removing the no-op guard, breaking the hand-written
brackets, re-expanding the indent). Each is caught by the test that claims to
guard it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ijuinryukichi
ijuinryukichi merged commit 324045f into main Sep 20, 2026
3 checks passed
@ijuinryukichi
ijuinryukichi deleted the feat/dump-segments branch September 20, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI: nothing writes the segments file that --segments reads

1 participant