How to make words in ASS subtitles appear one at a time?
Asked Answered
S

1

7

I am trying to burn subtitles into a video such that they appear in a word-by-word fashion instead of all at once.

What I mean by this is, a word will appear, then another word will appear next to it, and so on. Eventually the line will clear, then repeat.

Example:

A video shows a person speaking about chess. Subtitles at the bottom of the screen say “winning”. Moments later, the subtitles change to “winning a”, then “winning a piece”, later “winning a piece now” and so on. Each consecutive word instantly appears in whole, but each word only appears when the speaker says it.

I thought I could create an Advanced Substation Alpha file where subtitles share the same end-time but differing start times, however FFMPEG doesn't seem to cope very well when rendering the file:

[Script Info]
; Script generated by FFmpeg/Lavc57.107.100
ScriptType: v4.00+
PlayResX: 384
PlayResY: 288

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:03.46,Default,,0,0,0,,I'm
Dialogue: 0,0:00:01.00,0:00:03.46,Default,,0,0,0,,a
Dialogue: 0,0:00:01.50,0:00:03.46,Default,,0,0,0,,subtitle

The idea being that I'm would appear, then 1 second later a would show up next to it followed by subtitle a half second later

Sycophant answered 9/3, 2020 at 21:44 Comment(3)
Subtitle rendering is carried out by libass, not ffmpeg. Try their issue tracker.Ganiats
In your case, though, I believe the line construction is the issue. Instead of multiple entries for each word, there should be one entry for the whole line, with inline tags applied to delay appearance of each word.Ganiats
@Ganiats Thanks, I have created an issue on their tracker, I'll report back here if someone answers github.com/libass/libass/issues/378Sycophant
L
13

You’re on the right track, but what you’re doing now is creating new lines of text. They will, in fact, be shown next to each other, but they will be stacked vertically, which is not quite what you want.

Multiple events with repeated text

To add words to the same line, you need to stop the previous Dialogue event when the next word must appear and repeat the entire visible text in the next Dialogue event:

Dialogue: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,I'm
Dialogue: 0,0:00:01.00,0:00:01.50,Default,,0,0,0,,I'm a
Dialogue: 0,0:00:01.50,0:00:03.46,Default,,0,0,0,,I'm a subtitle

In case of centre- or (for languages written left-to-right) right-aligned text, or if your text is so long it automatically wraps around and is displayed on several lines, you’ll want to specify the entire text each time so the alignment and wrapping calculations work the same way on each event, but make the yet-invisible part of the text actually invisible by turning it fully transparent:

Dialogue: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,I'm {\alpha&HFF}a subtitle
Dialogue: 0,0:00:01.00,0:00:01.50,Default,,0,0,0,,I'm a {\alpha&HFF}subtitle
Dialogue: 0,0:00:01.50,0:00:03.46,Default,,0,0,0,,I'm a subtitle

This is the way it is usually done.

Single event with override tags

Alternatively, if your text has no border/outline and no shadow (not the case in your sample video), you can use the ASS karaoke tags by setting the yet-to-be-sung text colour (SecondaryColour) to fully transparent:

Dialogue: 0,0:00:00.00,0:00:03.46,Default,,0,0,0,,{\2a&HFF\k100}I'm {\k50}a {\k196}subtitle

If you do have a border or a shadow (as in the sample video), plain karaoke is not enough as you also want to modify the border/shadow colour. You can do this using ASS’s generic animation tag \t:

Dialogue: 0,0:00:00.00,0:00:03.46,Default,,0,0,0,,I'm {\alpha&HFF\t(1000,1000,\alpha0)}a {\alpha&HFF\t(1500,1500,\alpha0)}subtitle

Here, for each word except the first, I set all alpha values to fully transparent when the event starts, and then I instantly switch it to fully opaque when the word is to appear.

Caution about right-to-left text

There is no indication in the question that you intend to use this with anything but left-to-right English text, but this deserves a word of caution nevertheless. (You may even think it is natural to assume the distinction doesn’t matter nowadays. Unfortunately, due to some bad decisions and habits from the past, it still does.)

If you try any of these approaches with right-to-left text (e. g. Arabic or Hebrew) except the first, simplest approach that has no override tags, you’ll find that the text gets split at override tags and the sections get reordered so that earlier sections are always to the left of later sections.

Even in the simplest case without any override tags, if your text starts or ends with some punctuation (or other direction-neutral characters), they will not match the text’s natural direction and appear reversed. You can fix this by inserting the Unicode bidirectional control character U+200F RIGHT-TO-LEFT MARK before any leading punctuation and after any trailing punctuation.

Furthermore, old versions of libass (one of the two major ASS renderers), namely older than the 0.16.0 release of May 2022, used the same word & glyph order as if you had a single line without any tags. If you expect that your subtitle file might sometimes be rendered using old builds of libass and you want it to look the same in all renderers, you can work around the differences by inserting Unicode bidirectional control characters around override tags to make old libass match the ASS-standard (albeit unnatural) text order.

Of course, if you’re only burning in or visualizing subtitles without intending to distribute the subtitle file, then you don’t have to worry about how they look in other renderers. FFmpeg uses libass, and all versions of libass support a special bidirectional mode if you put -1 (minus one) in a Style definition’s Encoding field. This triggers autodetection of base direction for each line and disables the forced left-to-right ordering of tag-delimited sections, so right-to-left lines work intuitively correctly (except where autodetection fails).

Leghorn answered 10/3, 2020 at 22:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.