Skip to content

Add still_clip(), frames_clip(), audio_concat() - #18

Merged
TroyHernandez merged 4 commits into
mainfrom
feat/clip-primitives
Jul 20, 2026
Merged

Add still_clip(), frames_clip(), audio_concat()#18
TroyHernandez merged 4 commits into
mainfrom
feat/clip-primitives

Conversation

@TroyHernandez

Copy link
Copy Markdown
Contributor

Three call-by-call primitives, the compost half of the slide-explainer pipeline (cornductor's rmd_to_otiod()) and the landing pad for programmatic frame-drawn animation.

  • still_clip() — one image → a video clip, static or animated by a deterministic Ken Burns pan/zoom. Both cases run the same zoompan path (eased zoom + anchor expressions, crop clamped to frame) and the same encode settings as crossfade_concat(), so outputs stay concat-compatible. Motion spec: zoom_from/zoom_to/from/to/ease (linear | smooth), tolerant of JSON-deserialized shapes.
  • frames_clip() — a numbered frame sequence → a video clip (-framerate + pattern input). This is what an OTIO ImageSequenceReference will lower through in the follow-up renderer PR.
  • audio_concat() — join audio files through the concat filter with exact anullsrc silences between (gap) and after (tail), normalizing rate/layout per input; built for assembling a narration bed from per-slide TTS takes.

Also fixes a latent main breakage: since #17, crossfade_concat() probed videos[1] fps unconditionally, so the single-clip dry_run path (exercised by test_crossfade.R:6 with a file.create fake) errored. The probe is now lazy — only joins, windows, or an audio overlay need the rate.

Internals are pure string builders (.kenburns_filter, .ease_expr, .still_size, .audio_concat_filter) unit-tested without ffmpeg; at_home tests render real testsrc2/sine fixtures and assert frame counts, dimensions, durations, and that Ken Burns actually moves pixels. Full suite: 167/167 with at_home on.

Follow-up PR teaches render_timeline() to pre-render still and image-sequence clips through these, plus cornball.* effect lowering.

Three call-by-call primitives for slide/scene assembly:

- still_clip(): one image -> a video clip, static or animated by a
  deterministic Ken Burns pan/zoom (zoompan with eased zoom + anchor
  expressions, crop clamped to frame). Same encode settings as
  crossfade_concat() so outputs stay concat-compatible.
- frames_clip(): a numbered frame sequence -> a video clip; the landing
  pad for programmatically drawn animations.
- audio_concat(): join audio files through the concat filter with exact
  anullsrc silences between and after them, normalizing rate/layout;
  built for assembling a narration bed from per-segment TTS takes.
Since the frame-exact chain (#17), crossfade_concat probed videos[1]
unconditionally, so the single-clip dry_run path errored on files that
don't exist yet (test_crossfade.R line 6 has been failing since). Only
probe when a join, window, or audio overlay actually needs the rate.
@TroyHernandez
TroyHernandez merged commit 0ee6782 into main Jul 20, 2026
2 checks passed
@TroyHernandez
TroyHernandez deleted the feat/clip-primitives branch July 20, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant