Skip to content

Add LRC and TTML lyrics import - #699

Open
samgum wants to merge 1 commit into
TypesettingTools:masterfrom
samgum:upstream-pr/lrc-ttml-import
Open

Add LRC and TTML lyrics import#699
samgum wants to merge 1 commit into
TypesettingTools:masterfrom
samgum:upstream-pr/lrc-ttml-import

Conversation

@samgum

@samgum samgum commented Sep 2, 2026

Copy link
Copy Markdown

Here are two new readers for importing lyrics files, aimed at the word-timed formats people use for karaoke work. Both are import only. LRC and TTML can't represent the karaoke tags these imports produce, so a writer would silently throw that data away.

The LRC reader handles plain [mm:ss.xx] files as well as the enhanced variant with <mm:ss.xx> word markers, which become \kf tags so an import lands directly in the karaoke editor. A few behaviors came out of testing against real exports. Multi-timestamp lines expand to one row each, [offset:] is honored, and the trailing bare word timestamp Apple Music puts at the end of a line is read as that line's end time; without it, final words stretch across instrumental gaps and end up with durations like \k4595. Rows are also clamped so no two of them overlap, since layered echo lines can otherwise end after the next line has started. Files that turn out to have no timestamps at all, just lyric text under an .lrc name, import as untimed rows the way the plain text reader does.

The TTML reader walks <p> paragraphs and maps word-level <span begin/end> to \kf, keeping each span's own end time when it carries one. Background vocals marked ttm:role="x-bg" get a row of their own when they enter before the lead vocal finishes, because a single \kf chain can't hold two parallel voices without mangling word order or cutting held notes short. Harmony that comes in after the lead is done stays inline. Untimed exports (itunes:timing="None", bare text paragraphs) import as untimed rows, and line-level exports (itunes:timing="Line", no word spans) come in as plain rows.

I checked both readers against a folder of 386 Apple Music exports, roughly 17,600 rows, covering every variant above. The check re-implements the parsing rules line by line and looks for empty rows, overlapping times, negative durations and obvious outliers; all files pass. Two scope calls are open to discussion, namely import only for the reasons above, and LRC row ends clamping to the next row's start since LRC carries no explicit end data beyond that trailing marker.

Style catalog options follow the per-format convention (Subtitle Format/LRC and /TTML), and .lrc/.ttml/.dfxp were added to the drag-and-drop file list.

There is some overlap with #590, which adds a plain LRC reader. Beyond the word-level support, two behaviors differ that seemed worth mentioning. This PR expands multi-timestamp lines instead of leaving the second timestamp in the text, and it doesn't assume exactly two fractional digits when parsing times, so [01:53.5] comes out as 500 ms rather than 50.

Closes #698.

Adds two new subtitle readers aimed at word-timed lyrics files:

LRC (subtitle_format_lrc.cpp)
- Plain LRC: [mm:ss.xx] lines, multi-timestamp expansion, [offset:],
  1/2/3-digit fractional precision.
- Enhanced (syllable) LRC: inline <mm:ss.xx> word markers become ASS
  \kf karaoke segments so imports are karaoke-ready; a trailing bare
  word timestamp marks where the line ends, so held final notes keep
  their real duration and never stretch across instrumental gaps.
- Rows never overlap: echo/harmony layers whose end markers run past
  the next line's start are clamped.
- Files with no timestamps at all (plain lyric text exported as .lrc)
  import as untimed rows like the plain-text reader.

TTML (subtitle_format_ttml.cpp)
- Paragraph <p begin/end|dur> rows; word-level <span begin/end>
  timing becomes \kf karaoke with per-word end times honored.
- Namespaced documents matched by local name; <br/> becomes \N.
- Apple Music background vocals (ttm:role="x-bg") that enter while
  the lead voice is still singing are split into their own row with
  their real word timings (one gapless \kf chain cannot represent
  two parallel voices without scrambling word order); sequential
  harmony stays inline.
- itunes:timing="None" exports (untimed <p>) import as untimed
  rows; line-level exports (no word spans) import as plain rows.

Both readers were audited against 386 real Apple Music exports
(~17.6k rows, every variant above represented) with a line-by-line
reimplementation of the parsing rules: no empty rows, no overlapping
rows, no negative or runaway durations.

Style catalog options follow the existing per-format convention
(Subtitle Format/LRC and /TTML in default_config.json + preferences),
and .lrc/.ttml/.dfxp are accepted by the drag-and-drop file list.
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.

Import support for word-timed lyrics files (LRC / TTML)

1 participant