lacma-2026: VO + subtitle pipeline — tools/vo-pipeline.mjs
New reusable tool at tools/vo-pipeline.mjs that takes a timestamped
narration script + a source video and produces a narrated, captioned
version of the video with both baked in.
Pipeline:
1. Parse script (markdown with [M:SS] timestamps + # meta headers)
2. For each segment: macOS `say -v Daniel` generates AIFF, ffmpeg
converts to 48kHz/stereo WAV, probes duration
3. Build a full-length VO track by adelay-ing each chunk and amix-ing
4. Generate SRT cues from script timestamps
5. ffmpeg renders final: source video + (source audio ducked -18dB)
mixed with VO + libass subtitles filter burns SRT into the image
Requires ffmpeg-full (libass + libfreetype + drawtext filters).
macOS `say` handles TTS locally — no API keys, no cloud calls.
Usage:
node tools/vo-pipeline.mjs script.md --video in.mp4 --out out.mp4
node tools/vo-pipeline.mjs script.md --video in.mp4 --caption-only
node tools/vo-pipeline.mjs script.md --video in.mp4 --narrate-only
Flags:
--voice Daniel macOS say voice (default Daniel / en_GB)
--rate 175 speech rate in wpm
--duck -18 source-audio ducking under VO in dB
--keep-scratch keep /tmp intermediate files for debugging
First output:
grants/lacma-2026/demo-narration.md — 6-cue script for the 23s demo
→ ac-native-demo-narrated.{mp4,webm} on the CDN
Landing page: the demo section now has a two-tab toggle above the
video frame — "ambient loop" (the original, muted autoplay) and
"narrated + captioned" (the new pipeline output, unmutes on swap).
Tabs hot-swap the <video> sources so the URL stays the same.
Also updated the Video row in § Application at a Glance: both cuts
linked, with the pipeline tool cited as a reusable artifact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>