Library guide¶
syncalong is a library first: the CLI is a thin layer over the same public
API you can import. import syncalong is cheap — the heavy Whisper import is
deferred until a model is actually loaded — so importing the package in a
larger application (a jukebox, a tagging tool, a batch job) costs almost
nothing until you transcribe.
The full public surface is documented in the API reference. This page shows the common patterns.
Align a single song¶
align() runs the whole
pipeline and returns a structured
AlignmentResult:
import syncalong
from pathlib import Path
tx = syncalong.Transcriber(model_name="base") # load the model once
res = syncalong.align(Path("lyrics.txt"), "song.mp3", transcriber=tx)
print(res.lrc) # the LRC document, as a string
res.timed_lines # [(LyricLine, 12.3), (LyricLine, None), ...]
res.matched, res.total # e.g. (28, 30) — lines that got a timestamp / all lines
The lyrics argument is type-driven¶
align() decides what to do with lyrics based on its type:
| You pass… | syncalong… |
|---|---|
pathlib.Path |
reads and parses that lyrics file |
str |
parses it as raw lyrics text (not a path) |
list[LyricLine] |
uses your already-parsed lines as-is |
str means lyrics text, not a filename
Passing "lyrics.txt" as a plain string treats the characters
lyrics.txt as the song's lyrics. To read a file, wrap it in Path:
align(Path("lyrics.txt"), ...).
audio is always a path — a str or Path pointing at the audio file.
Keyword options¶
align() mirrors the CLI:
| Argument | Meaning | Default |
|---|---|---|
transcriber |
A pre-loaded Transcriber to reuse. If None, one is built from model_name. |
None |
model_name |
Whisper model to load when transcriber is None. |
"base" |
language |
BCP-47 language code, or None to auto-detect. |
None |
use_lyrics_prompt |
Bias Whisper with the lyrics text. | True |
threshold |
Minimum fuzzy score (0–100) to accept a word match. | 55.0 |
separate_vocals |
Isolate vocals with Demucs first (needs the extra). | False |
Just the LRC string¶
When you only want the LRC text,
align_to_lrc() is a
convenience wrapper — equivalent to align(...).lrc and it accepts the same
keyword arguments:
Batch-process an album (reuse the model)¶
Loading a Whisper model takes seconds, so a long-running job should create one
Transcriber and
reuse it across every song:
import syncalong
from pathlib import Path
tx = syncalong.Transcriber(model_name="small") # loaded once for the whole run
for audio in sorted(Path("album/").glob("*.flac")):
lyrics = audio.with_suffix(".txt")
lrc = syncalong.align_to_lrc(lyrics, audio, transcriber=tx)
audio.with_suffix(".lrc").write_text(lrc, encoding="utf-8")
print(f"wrote {audio.stem}.lrc")
Lyrics already in memory¶
If your lyrics come from a database or tag rather than a file, pass them as a plain string:
text = "I walk a lonely road\nThe only one that I have ever known\n"
res = syncalong.align(text, "song.mp3", transcriber=tx)
Working with the result¶
AlignmentResult.timed_lines
is a list of (LyricLine, timestamp_or_None) pairs, in order. Each
LyricLine keeps its original
raw text, its normalized words, and an is_blank flag for
instrumental/section lines. This lets you build your own UI instead of parsing
LRC back out:
res = syncalong.align(Path("lyrics.txt"), "song.mp3", transcriber=tx)
for line, ts in res.timed_lines:
if line.is_blank:
continue
when = f"{ts:6.2f}s" if ts is not None else " -- "
print(f"{when} {line.raw}")
print(f"{res.matched}/{res.total} lines aligned")
Assembling the pipeline yourself¶
For full control you can call the building blocks directly instead of
align(). This is what the facade does internally:
from pathlib import Path
from syncalong import (
Transcriber,
parse_lyrics,
lyrics_prompt,
align_lyrics_to_transcript,
format_lrc,
)
lines = parse_lyrics(Path("lyrics.txt"))
tx = Transcriber(model_name="base")
transcript = tx.transcribe(
Path("song.mp3"),
initial_prompt=lyrics_prompt(lines), # bias Whisper toward the known words
)
timed = align_lyrics_to_transcript(lines, transcript, threshold=55.0)
lrc = format_lrc(timed)
You could, for example, swap in your own transcript (any
list[WordTimestamp]) and reuse only the aligner and formatter.
Injecting a Whisper model¶
Transcriber accepts a preloaded model via the keyword-only model argument.
Pass a model you already loaded to share it, or a fake object in tests — no
Whisper download required:
When model is given, model_name is ignored and Whisper is never loaded.
Errors to expect¶
| Situation | Raised |
|---|---|
lyrics is not a Path, str, or list |
TypeError |
separate_vocals=True but Demucs isn't installed |
ModuleNotFoundError |
| Audio file can't be decoded | error from Whisper/ffmpeg |
Unlike the CLI (which prints to stderr and calls sys.exit), the library raises
exceptions so your application stays in control of error handling.