@audio/ subtitle
Status pending calibration
npm0.1.1licenseMIT
Timed-text subtitle formats — SRT, WebVTT, LRC (+ enhanced word-level), TTML: parse and write
Overview
- Install
- Updated
- Family
- midi, 5 packages under it
- Tags
- read & write, make
Timed-text subtitle formats — SRT, WebVTT, LRC, TTML: parse and write
import { parse, write } from '@audio/subtitle'
Reads and writes four timed-text formats to one { cues, meta?, format } shape: SRT
(de facto SubRip, no formal spec), WebVTT (W3C),
LRC (de facto lyrics format; word-level timing is the A2/MiniLyrics
"enhanced" extension), and TTML
(W3C Timed Text Markup Language 2, a minimal profile:
<tt><body><div><p begin end>). Format is auto-detected on parse. Zero dependencies —
TTML is walked with a small hand-rolled tag tokenizer, not a DOM parser.
let subs = parse(srtText) // auto-detects 'srt'
subs.cues // [{ start, end, text, id?, words?, ... }]
write(subs, 'vtt') // -> WebVTT text
write(subs.cues, 'lrc', { words: true }) // bare Cue[] also accepted; word-level tags
Every cue is { start, end, text, id?, words?, settings?, style? } — start/end in
seconds, text plain with \n line breaks (VTT <b>/<i>/<c.x>/<v> and TTML
<span> wrappers are stripped, not retained). words?: {start, end?, text}[] carries
word-level timing when the source has it: VTT/LRC inline <00:01.000> tags (chained —
each word's end is the next word's start) and TTML nested <span begin end> (independent
per-word begin/end). meta carries format-specific header data: LRC ti/ar/al/by/
offset/length tags (pass-through, any key), VTT the text after WEBVTT, TTML
xml:lang and ttm:title/desc/copyright.
import { shift, scale, project, fromWords } from '@audio/subtitle'
shift(subs, 1.5) // +1.5s to every cue and word
scale(subs, 48000 / 44100) // resample-class tempo change
project(subs, desilenceMap) // desilence-style [{from,to}] warp; dropped-span cues removed
fromWords(alignerWords) // ASR/aligner word timings -> readable cues
shift/scale/project accept either a {cues,...} object or a bare Cue[] and return
the same shape. project(subs, map) linearly interpolates cue (and word) times through a
piecewise [{from,to}] map — the shape @audio/denoise's
silence-removal pass returns — and drops any cue that falls entirely inside a map segment
whose to doesn't advance (a removed span).
fromWords(words, opts?) segments raw word timings ({text|word, start, end}, e.g. from
a forced aligner) into cues, following the
Netflix Timed Text Style Guide
line-length convention: breaks first at a ≥maxGap silence or trailing punctuation, and
only falls back to a hard cut at maxChars/maxDuration when neither is available.
| Option | Default | |
|---|---|---|
maxChars |
42 |
max characters per line |
maxDuration |
6 |
max cue duration, seconds |
maxGap |
1.0 |
silence between words that forces a break, seconds |
lines |
2 |
max lines per cue |
Time formatting is deterministic: milliseconds round half-up, decomposed once from a
single rounded total (no double-rounding drift). SRT/VTT/TTML always write hh:mm:ss with
hours (VTT's spec allows omitting a zero hour; this always includes it — simpler and
unambiguous for players that are strict about the field count). LRC writes mm:ss.xx
(centiseconds; opts.precision: 3 for milliseconds), minutes unbounded rather than rolling
into hours, matching the format's convention. LRC's offset tag is applied per the spec
("+" plays lyrics sooner, i.e. subtracted from the raw tag time) — write re-adds it, so
parse(write(x)) round-trips through an offset tag without double-shifting.
TTML parsing accepts clock-time (00:00:01.500), frame notation (00:00:01:12 with
ttp:frameRate), and offset-time (1.5s, 500ms, 2m, 25f); writing always emits
plain clock-time. write never emits xml:space or wraps <p> content across lines —
keep cue text free of incidental whitespace if hand-editing TTML for round-tripping.
Use when: importing/exporting captions or lyrics around any of the other atoms — ASR
transcripts (fromWords), ffmpeg/media pipelines, or timeline edits that shift/desilence
audio and need the subtitle track to follow.
Part of @audio/midi — the midi family umbrella (symbolic & timed formats: SMF, MusicXML, subtitles).
MIT © audiojs