;; process-pair template ;; ;; Reusable video-pair processor: takes a single video source, creates two ;; clips (A and B) with opposite rotations and sporadic effects, blends them, ;; and applies a per-pair slow rotation driven by a beat scan. ;; ;; All sporadic triggers (invert, hue-shift, ascii) and pair-level controls ;; (blend opacity, rotation) are defined internally using seed offsets. ;; ;; Parameters: ;; video - source video node ;; energy - energy analysis node (drives rotation/zoom amounts) ;; beat-data - beat analysis node (drives sporadic triggers) ;; rng - RNG object from (make-rng seed) for auto-derived seeds ;; rot-dir - initial rotation direction: 1 (clockwise) or -1 (anti-clockwise) ;; rot-a/b - rotation ranges for clip A/B (e.g. [0 45]) ;; zoom-a/b - zoom ranges for clip A/B (e.g. [1 1.5]) (deftemplate process-pair (video energy beat-data rng rot-dir rot-a rot-b zoom-a zoom-b) ;; --- Sporadic triggers for clip A --- ;; Invert: 10% chance per beat, lasts 1-5 beats (def inv-a (scan beat-data :rng rng :init 0 :step (if (> acc 0) (- acc 1) (if (< (rand) 0.1) (rand-int 1 5) 0)) :emit (if (> acc 0) 1 0))) ;; Hue shift: 10% chance, random hue 30-330 deg, lasts 1-5 beats (def hue-a (scan beat-data :rng rng :init (dict :rem 0 :hue 0) :step (if (> rem 0) (dict :rem (- rem 1) :hue hue) (if (< (rand) 0.1) (dict :rem (rand-int 1 5) :hue (rand-range 30 330)) (dict :rem 0 :hue 0))) :emit (if (> rem 0) hue 0))) ;; ASCII art: 5% chance, lasts 1-3 beats (def ascii-a (scan beat-data :rng rng :init 0 :step (if (> acc 0) (- acc 1) (if (< (rand) 0.05) (rand-int 1 3) 0)) :emit (if (> acc 0) 1 0))) ;; --- Sporadic triggers for clip B (offset seeds) --- (def inv-b (scan beat-data :rng rng :init 0 :step (if (> acc 0) (- acc 1) (if (< (rand) 0.1) (rand-int 1 5) 0)) :emit (if (> acc 0) 1 0))) (def hue-b (scan beat-data :rng rng :init (dict :rem 0 :hue 0) :step (if (> rem 0) (dict :rem (- rem 1) :hue hue) (if (< (rand) 0.1) (dict :rem (rand-int 1 5) :hue (rand-range 30 330)) (dict :rem 0 :hue 0))) :emit (if (> rem 0) hue 0))) (def ascii-b (scan beat-data :rng rng :init 0 :step (if (> acc 0) (- acc 1) (if (< (rand) 0.05) (rand-int 1 3) 0)) :emit (if (> acc 0) 1 0))) ;; --- Pair-level controls --- ;; Internal A/B blend: randomly show A (0), both (0.5), or B (1), every 1-11 beats (def pair-mix (scan beat-data :rng rng :init (dict :rem 0 :opacity 0.5) :step (if (> rem 0) (dict :rem (- rem 1) :opacity opacity) (dict :rem (rand-int 1 11) :opacity (* (rand-int 0 2) 0.5))) :emit opacity)) ;; Per-pair rotation: one full rotation every 20-30 beats, alternating direction (def pair-rot (scan beat-data :rng rng :init (dict :beat 0 :clen 25 :dir rot-dir :angle 0) :step (if (< (+ beat 1) clen) (dict :beat (+ beat 1) :clen clen :dir dir :angle (+ angle (* dir (/ 360 clen)))) (dict :beat 0 :clen (rand-int 20 30) :dir (* dir -1) :angle angle)) :emit angle)) ;; --- Clip A processing --- (def clip-a (-> video (segment :start 0 :duration (bind energy duration)))) (def rotated-a (-> clip-a (effect rotate :angle (bind energy values :range rot-a)) (effect zoom :amount (bind energy values :range zoom-a)) (effect invert :amount (bind inv-a values)) (effect hue_shift :degrees (bind hue-a values)) ;; ASCII disabled - too slow without GPU ;; (effect ascii_art ;; :char_size (bind energy values :range [4 32]) ;; :mix (bind ascii-a values)) )) ;; --- Clip B processing --- (def clip-b (-> video (segment :start 0 :duration (bind energy duration)))) (def rotated-b (-> clip-b (effect rotate :angle (bind energy values :range rot-b)) (effect zoom :amount (bind energy values :range zoom-b)) (effect invert :amount (bind inv-b values)) (effect hue_shift :degrees (bind hue-b values)) ;; ASCII disabled - too slow without GPU ;; (effect ascii_art ;; :char_size (bind energy values :range [4 32]) ;; :mix (bind ascii-b values)) )) ;; --- Blend A+B and apply pair rotation --- (-> rotated-a (effect blend rotated-b :mode "alpha" :opacity (bind pair-mix values) :resize_mode "fit") (effect rotate :angle (bind pair-rot values))))