muvid.lyricvid.timed_text¶
The timed text tree — song → sections → lines → words, with measured times.
Everything downstream reads this and nothing else, which is what lets the subgenre accept four quite different kinds of input without the renderers caring which one arrived:
input the caller has |
how it gets here |
|---|---|
a muvid project |
|
an |
|
lyrics text + audio |
|
audio only |
the aligner’s transcription path, same function. |
Line-level input is not word-level input, and pretending otherwise is how lyric
videos end up subtly out of sync. When words are interpolated inside a line
rather than measured, Word.measured is False — renderers and the
quantiser can then prefer line quantisation, and a caller can be told the
timing is approximate instead of discovering it in the render.
Import-safe: stdlib only at module scope. The muvid alignment machinery and any ASR are imported inside the functions that need them.
Functions
|
Read muvid's own three-tier alignment (sections / lines / words). |
|
Convert a |
|
Read |
|
Align |
|
Build from a flat |
Classes
|
One sung word on the song timeline. |
|
One sung line. |
|
A labelled span — |
|
The whole song's text, timed. |
- class muvid.lyricvid.timed_text.Line(*, words, index=0, text='')[source]¶
Bases:
objectOne sung line.
- class muvid.lyricvid.timed_text.Section(*, label, lines)[source]¶
Bases:
objectA labelled span —
verse,chorus, whatever the lyrics document says.
- class muvid.lyricvid.timed_text.TimedText(*, sections, duration=0.0, source='unknown')[source]¶
Bases:
objectThe whole song’s text, timed.
>>> tt = from_words([('hello', 0.0, 0.5), ('world', 0.5, 1.0)], duration=1.0) >>> [w.text for w in tt.words()] ['hello', 'world'] >>> tt.sections[0].label '*'
- class muvid.lyricvid.timed_text.Word(*, text, start, end, measured=True, glyphs=())[source]¶
Bases:
objectOne sung word on the song timeline. Times are seconds, absolute.
- muvid.lyricvid.timed_text.from_alignment_result(result, *, duration=0.0, source='muvid-align')[source]¶
Convert a
muvid.align.AlignmentResultinto a timed tree.Keeps the sections and lines the aligner found — which is strictly better than flattening to words and re-splitting on gaps, because the lyrics document already knows where the lines are.
A line whose words the aligner could not match still has a
[start, end](interpolated by the aligner from its neighbours); its words are spread across that span and markedmeasured=False, so downstream can see the difference between a measured onset and a guessed one.- Return type:
>>> from types import SimpleNamespace as NS >>> w = NS(text='hi', start_s=0.0, end_s=0.4, token_index=0) >>> ln = NS(line_index=0, text='hi there', start_s=0.0, end_s=1.0, ... word_alignments=(w,)) >>> sec = NS(label='verse', lines=(ln,)) >>> tt = from_alignment_result(NS(sections=(sec,)), duration=1.0) >>> [(x.text, x.measured) for x in tt.words()] [('hi', True), ('there', False)] >>> tt.sections[0].label 'verse'
- muvid.lyricvid.timed_text.from_alignment_store(project_root, *, duration=0.0)[source]¶
Read muvid’s own three-tier alignment (sections / lines / words).
muvid already declares itself the word-timing SSOT —
muvid.alignwrites alacingstore andmuvid.contractsreads it — so this subgenre reads that store rather than growing a second transcription path.- Return type:
- muvid.lyricvid.timed_text.from_lyrics_and_audio(audio, *, lyrics=None, aligner=None, duration=0.0)[source]¶
Align
lyricstoaudiothrough muvid’s aligner registry.Goes through
muvid.align.align_lyrics()rather than calling an ASR directly, so a singing-grade aligner registered later is picked up here for free. The aligner is chosen honestly by what is installed:whisperx-lite— offline, free, needsfaster-whisper. **Default when available.** Runs on the audio itself.scribe-greedy— needs a Scribe transcript, which costs money and an ElevenLabs key; only reached when asked for by name, never as a silent fallback that spends.
With no
lyricsat all there is nothing to align to; the transcript’s own words become the text (source='transcript').alignermay be any registered name; unknown names raise frommuvid.alignwith the registered list.- Return type:
- muvid.lyricvid.timed_text.from_subtitles(path, *, duration=0.0)[source]¶
Read
.srt,.lrcor enhanced.lrcinto a timed tree.Enhanced LRC carries real per-word stamps and is used as such; plain LRC and SRT give line times only, so their words are spread and marked
measured=False.- Return type:
- muvid.lyricvid.timed_text.from_words(words, *, duration=0.0, line_gap_s=0.9, source='words')[source]¶
Build from a flat
(text, start, end)stream, splitting lines on gaps.The gap heuristic is deliberately simple and deliberately visible: a real lyrics document is always better, and when one exists the other builders use it instead of guessing.
- Return type: