Add streaming video compositor with sexp interpreter

- New streaming/ module for real-time video processing:
  - compositor.py: Main streaming compositor with cycle-crossfade
  - sexp_executor.py: Executes compiled sexp recipes in real-time
  - sexp_interp.py: Full S-expression interpreter for SLICE_ON Lambda
  - recipe_adapter.py: Bridges recipes to streaming compositor
  - sources.py: Video source with ffmpeg streaming
  - audio.py: Real-time audio analysis (energy, beats)
  - output.py: Preview (mpv) and file output with audio muxing

- New templates/:
  - cycle-crossfade.sexp: Smooth zoom-based video cycling
  - process-pair.sexp: Dual-clip processing with effects

- Key features:
  - Videos cycle in input-videos order (not definition order)
  - Cumulative whole-spin rotation
  - Zero-weight sources skip processing
  - Live audio-reactive effects

- New effects: blend_multi for weighted layer compositing
- Updated primitives and interpreter for streaming compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-29 01:27:39 +00:00
parent 17e3e23f06
commit d241e2a663
31 changed files with 5143 additions and 96 deletions

View File

@@ -14,6 +14,8 @@
:desc "Number of character columns")
(rotation_scale :type float :default 60 :range [0 180]
:desc "Max rotation in degrees")
(duration :type float :default 10 :range [1 300]
:desc "Clip duration in seconds")
)
;; Registry
@@ -29,7 +31,7 @@
;; Stage 1: Analysis
(stage :analyze
:outputs [energy-data]
(def audio-clip (-> audio (segment :start 60 :duration 10)))
(def audio-clip (-> audio (segment :start 60 :duration duration)))
(def energy-data (-> audio-clip (analyze energy))))
;; Stage 2: Process
@@ -37,8 +39,8 @@
:requires [:analyze]
:inputs [energy-data]
:outputs [result audio-clip]
(def clip (-> video (segment :start 0 :duration 10)))
(def audio-clip (-> audio (segment :start 60 :duration 10)))
(def clip (-> video (segment :start 0 :duration duration)))
(def audio-clip (-> audio (segment :start 60 :duration duration)))
(def result (-> clip
(effect ascii_fx_zone