Import L1 (celery) as l1/
This commit is contained in:
223
l1/recipes/woods-lowres.sexp
Normal file
223
l1/recipes/woods-lowres.sexp
Normal file
@@ -0,0 +1,223 @@
|
||||
;; Woods Recipe - OPTIMIZED VERSION
|
||||
;;
|
||||
;; Uses fused-pipeline for GPU acceleration when available,
|
||||
;; falls back to individual primitives on CPU.
|
||||
;;
|
||||
;; Key optimizations:
|
||||
;; 1. Uses streaming_gpu primitives with fast CUDA kernels
|
||||
;; 2. Uses fused-pipeline to batch effects into single kernel passes
|
||||
;; 3. GPU persistence - frames stay on GPU throughout pipeline
|
||||
|
||||
(stream "woods-lowres"
|
||||
:fps 30
|
||||
:width 640
|
||||
:height 360
|
||||
:seed 42
|
||||
|
||||
;; Load standard primitives (includes proper asset resolution)
|
||||
;; Auto-selects GPU versions when available, falls back to CPU
|
||||
(include :name "tpl-standard-primitives")
|
||||
|
||||
;; === SOURCES (using streaming: which has proper asset resolution) ===
|
||||
(def sources [
|
||||
(streaming:make-video-source "woods-1" 30)
|
||||
(streaming:make-video-source "woods-2" 30)
|
||||
(streaming:make-video-source "woods-3" 30)
|
||||
(streaming:make-video-source "woods-4" 30)
|
||||
(streaming:make-video-source "woods-5" 30)
|
||||
(streaming:make-video-source "woods-6" 30)
|
||||
(streaming:make-video-source "woods-7" 30)
|
||||
(streaming:make-video-source "woods-8" 30)
|
||||
])
|
||||
|
||||
;; Per-pair config
|
||||
(def pair-configs [
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 30 :rot-b -30 :zoom-a 1.3 :zoom-b 0.7}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
])
|
||||
|
||||
;; Audio
|
||||
(def music (streaming:make-audio-analyzer "woods-audio"))
|
||||
(audio-playback "woods-audio")
|
||||
|
||||
;; === SCANS ===
|
||||
|
||||
;; Cycle state
|
||||
(scan cycle (streaming:audio-beat music t)
|
||||
:init {:active 0 :beat 0 :clen 16}
|
||||
:step (if (< (+ beat 1) clen)
|
||||
(dict :active active :beat (+ beat 1) :clen clen)
|
||||
(dict :active (mod (+ active 1) (len sources)) :beat 0
|
||||
:clen (+ 8 (mod (* (streaming:audio-beat-count music t) 7) 17)))))
|
||||
|
||||
;; Spin scan
|
||||
(scan spin (streaming:audio-beat music t)
|
||||
:init {:angle 0 :dir 1 :speed 2}
|
||||
:step (let [new-dir (if (< (core:rand) 0.05) (* dir -1) dir)
|
||||
new-speed (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) speed)]
|
||||
(dict :angle (+ angle (* new-dir new-speed))
|
||||
:dir new-dir
|
||||
:speed new-speed)))
|
||||
|
||||
;; Ripple scan - raindrop style, all params randomized
|
||||
;; Higher freq = bigger gaps between waves (formula is dist/freq)
|
||||
(scan ripple-state (streaming:audio-beat music t)
|
||||
:init {:gate 0 :cx 320 :cy 180 :freq 20 :decay 6 :amp-mult 1.0}
|
||||
:step (let [new-gate (if (< (core:rand) 0.2) (+ 2 (core:rand-int 0 4)) (core:max 0 (- gate 1)))
|
||||
triggered (> new-gate gate)
|
||||
new-cx (if triggered (core:rand-int 50 590) cx)
|
||||
new-cy (if triggered (core:rand-int 50 310) cy)
|
||||
new-freq (if triggered (+ 15 (core:rand-int 0 20)) freq)
|
||||
new-decay (if triggered (+ 5 (core:rand-int 0 4)) decay)
|
||||
new-amp-mult (if triggered (+ 0.8 (* (core:rand) 1.2)) amp-mult)]
|
||||
(dict :gate new-gate :cx new-cx :cy new-cy :freq new-freq :decay new-decay :amp-mult new-amp-mult)))
|
||||
|
||||
;; Pair states
|
||||
(scan pairs (streaming:audio-beat music t)
|
||||
:init {:states (map (core:range (len sources)) (lambda (_)
|
||||
{:inv-a 0 :inv-b 0 :hue-a 0 :hue-b 0 :hue-a-val 0 :hue-b-val 0 :mix 0.5 :mix-rem 5 :angle 0 :rot-beat 0 :rot-clen 25}))}
|
||||
:step (dict :states (map states (lambda (p)
|
||||
(let [new-inv-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-a) 1)))
|
||||
new-inv-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-b) 1)))
|
||||
old-hue-a (get p :hue-a)
|
||||
old-hue-b (get p :hue-b)
|
||||
new-hue-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-a 1)))
|
||||
new-hue-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-b 1)))
|
||||
new-hue-a-val (if (> new-hue-a old-hue-a) (+ 30 (* (core:rand) 300)) (get p :hue-a-val))
|
||||
new-hue-b-val (if (> new-hue-b old-hue-b) (+ 30 (* (core:rand) 300)) (get p :hue-b-val))
|
||||
mix-rem (get p :mix-rem)
|
||||
old-mix (get p :mix)
|
||||
new-mix-rem (if (> mix-rem 0) (- mix-rem 1) (+ 1 (core:rand-int 1 10)))
|
||||
new-mix (if (> mix-rem 0) old-mix (* (core:rand-int 0 2) 0.5))
|
||||
rot-beat (get p :rot-beat)
|
||||
rot-clen (get p :rot-clen)
|
||||
old-angle (get p :angle)
|
||||
new-rot-beat (if (< (+ rot-beat 1) rot-clen) (+ rot-beat 1) 0)
|
||||
new-rot-clen (if (< (+ rot-beat 1) rot-clen) rot-clen (+ 20 (core:rand-int 0 10)))
|
||||
new-angle (+ old-angle (/ 360 rot-clen))]
|
||||
(dict :inv-a new-inv-a :inv-b new-inv-b
|
||||
:hue-a new-hue-a :hue-b new-hue-b
|
||||
:hue-a-val new-hue-a-val :hue-b-val new-hue-b-val
|
||||
:mix new-mix :mix-rem new-mix-rem
|
||||
:angle new-angle :rot-beat new-rot-beat :rot-clen new-rot-clen))))))
|
||||
|
||||
;; === OPTIMIZED PROCESS-PAIR MACRO ===
|
||||
;; Uses fused-pipeline to batch rotate+hue+invert into single kernel
|
||||
(defmacro process-pair-fast (idx)
|
||||
(let [;; Get sources for this pair (with safe modulo indexing)
|
||||
num-sources (len sources)
|
||||
src-a (nth sources (mod (* idx 2) num-sources))
|
||||
src-b (nth sources (mod (+ (* idx 2) 1) num-sources))
|
||||
cfg (nth pair-configs idx)
|
||||
pstate (nth (bind pairs :states) idx)
|
||||
|
||||
;; Read frames (GPU decode, stays on GPU)
|
||||
frame-a (streaming:source-read src-a t)
|
||||
frame-b (streaming:source-read src-b t)
|
||||
|
||||
;; Get state values
|
||||
dir (get cfg :dir)
|
||||
rot-max-a (get cfg :rot-a)
|
||||
rot-max-b (get cfg :rot-b)
|
||||
zoom-max-a (get cfg :zoom-a)
|
||||
zoom-max-b (get cfg :zoom-b)
|
||||
pair-angle (get pstate :angle)
|
||||
inv-a-on (> (get pstate :inv-a) 0)
|
||||
inv-b-on (> (get pstate :inv-b) 0)
|
||||
hue-a-on (> (get pstate :hue-a) 0)
|
||||
hue-b-on (> (get pstate :hue-b) 0)
|
||||
hue-a-val (get pstate :hue-a-val)
|
||||
hue-b-val (get pstate :hue-b-val)
|
||||
mix-ratio (get pstate :mix)
|
||||
|
||||
;; Calculate rotation angles
|
||||
angle-a (* dir pair-angle rot-max-a 0.01)
|
||||
angle-b (* dir pair-angle rot-max-b 0.01)
|
||||
|
||||
;; Energy-driven zoom (maps audio energy 0-1 to 1-max)
|
||||
zoom-a (core:map-range e 0 1 1 zoom-max-a)
|
||||
zoom-b (core:map-range e 0 1 1 zoom-max-b)
|
||||
|
||||
;; Define effect pipelines for each source
|
||||
;; These get compiled to single CUDA kernels!
|
||||
;; First resize to target resolution, then apply effects
|
||||
effects-a [{:op "resize" :width 640 :height 360}
|
||||
{:op "zoom" :amount zoom-a}
|
||||
{:op "rotate" :angle angle-a}
|
||||
{:op "hue_shift" :degrees (if hue-a-on hue-a-val 0)}
|
||||
{:op "invert" :amount (if inv-a-on 1 0)}]
|
||||
effects-b [{:op "resize" :width 640 :height 360}
|
||||
{:op "zoom" :amount zoom-b}
|
||||
{:op "rotate" :angle angle-b}
|
||||
{:op "hue_shift" :degrees (if hue-b-on hue-b-val 0)}
|
||||
{:op "invert" :amount (if inv-b-on 1 0)}]
|
||||
|
||||
;; Apply fused pipelines (single kernel per source!)
|
||||
processed-a (streaming:fused-pipeline frame-a effects-a)
|
||||
processed-b (streaming:fused-pipeline frame-b effects-b)]
|
||||
|
||||
;; Blend the two processed frames
|
||||
(blending:blend-images processed-a processed-b mix-ratio)))
|
||||
|
||||
;; === FRAME PIPELINE ===
|
||||
(frame
|
||||
(let [now t
|
||||
e (streaming:audio-energy music now)
|
||||
|
||||
;; Get cycle state
|
||||
active (bind cycle :active)
|
||||
beat-pos (bind cycle :beat)
|
||||
clen (bind cycle :clen)
|
||||
|
||||
;; Transition logic
|
||||
phase3 (* beat-pos 3)
|
||||
fading (and (>= phase3 (* clen 2)) (< phase3 (* clen 3)))
|
||||
fade-amt (if fading (/ (- phase3 (* clen 2)) clen) 0)
|
||||
next-idx (mod (+ active 1) (len sources))
|
||||
|
||||
;; Process active pair with fused pipeline
|
||||
active-frame (process-pair-fast active)
|
||||
|
||||
;; Crossfade with zoom during transition
|
||||
;; Old pair: zooms out (1.0 -> 2.0) and fades out
|
||||
;; New pair: starts small (0.1), zooms in (-> 1.0) and fades in
|
||||
result (if fading
|
||||
(let [next-frame (process-pair-fast next-idx)
|
||||
;; Active zooms out as it fades
|
||||
active-zoom (+ 1.0 fade-amt)
|
||||
active-zoomed (streaming:fused-pipeline active-frame
|
||||
[{:op "zoom" :amount active-zoom}])
|
||||
;; Next starts small and zooms in
|
||||
next-zoom (+ 0.1 (* fade-amt 0.9))
|
||||
next-zoomed (streaming:fused-pipeline next-frame
|
||||
[{:op "zoom" :amount next-zoom}])]
|
||||
(blending:blend-images active-zoomed next-zoomed fade-amt))
|
||||
active-frame)
|
||||
|
||||
;; Final effects pipeline (fused!)
|
||||
spin-angle (bind spin :angle)
|
||||
;; Ripple params - all randomized per ripple trigger
|
||||
rip-gate (bind ripple-state :gate)
|
||||
rip-amp-mult (bind ripple-state :amp-mult)
|
||||
rip-amp (* rip-gate rip-amp-mult (core:map-range e 0 1 50 200))
|
||||
rip-cx (bind ripple-state :cx)
|
||||
rip-cy (bind ripple-state :cy)
|
||||
rip-freq (bind ripple-state :freq)
|
||||
rip-decay (bind ripple-state :decay)
|
||||
|
||||
;; Fused final effects
|
||||
final-effects [{:op "rotate" :angle spin-angle}
|
||||
{:op "ripple" :amplitude rip-amp :frequency rip-freq :decay rip-decay
|
||||
:phase (* now 5) :center_x rip-cx :center_y rip-cy}]]
|
||||
|
||||
;; Apply final fused pipeline
|
||||
(streaming:fused-pipeline result final-effects
|
||||
:rotate_angle spin-angle
|
||||
:ripple_phase (* now 5)
|
||||
:ripple_amplitude rip-amp))))
|
||||
211
l1/recipes/woods-recipe-optimized.sexp
Normal file
211
l1/recipes/woods-recipe-optimized.sexp
Normal file
@@ -0,0 +1,211 @@
|
||||
;; Woods Recipe - OPTIMIZED VERSION
|
||||
;;
|
||||
;; Uses fused-pipeline for GPU acceleration when available,
|
||||
;; falls back to individual primitives on CPU.
|
||||
;;
|
||||
;; Key optimizations:
|
||||
;; 1. Uses streaming_gpu primitives with fast CUDA kernels
|
||||
;; 2. Uses fused-pipeline to batch effects into single kernel passes
|
||||
;; 3. GPU persistence - frames stay on GPU throughout pipeline
|
||||
|
||||
(stream "woods-recipe-optimized"
|
||||
:fps 30
|
||||
:width 1920
|
||||
:height 1080
|
||||
:seed 42
|
||||
|
||||
;; Load standard primitives (includes proper asset resolution)
|
||||
;; Auto-selects GPU versions when available, falls back to CPU
|
||||
(include :name "tpl-standard-primitives")
|
||||
|
||||
;; === SOURCES (using streaming: which has proper asset resolution) ===
|
||||
(def sources [
|
||||
(streaming:make-video-source "woods-1" 30)
|
||||
(streaming:make-video-source "woods-2" 30)
|
||||
(streaming:make-video-source "woods-3" 30)
|
||||
(streaming:make-video-source "woods-4" 30)
|
||||
(streaming:make-video-source "woods-5" 30)
|
||||
(streaming:make-video-source "woods-6" 30)
|
||||
(streaming:make-video-source "woods-7" 30)
|
||||
(streaming:make-video-source "woods-8" 30)
|
||||
])
|
||||
|
||||
;; Per-pair config
|
||||
(def pair-configs [
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 30 :rot-b -30 :zoom-a 1.3 :zoom-b 0.7}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
])
|
||||
|
||||
;; Audio
|
||||
(def music (streaming:make-audio-analyzer "woods-audio"))
|
||||
(audio-playback "woods-audio")
|
||||
|
||||
;; === SCANS ===
|
||||
|
||||
;; Cycle state
|
||||
(scan cycle (streaming:audio-beat music t)
|
||||
:init {:active 0 :beat 0 :clen 16}
|
||||
:step (if (< (+ beat 1) clen)
|
||||
(dict :active active :beat (+ beat 1) :clen clen)
|
||||
(dict :active (mod (+ active 1) (len sources)) :beat 0
|
||||
:clen (+ 8 (mod (* (streaming:audio-beat-count music t) 7) 17)))))
|
||||
|
||||
;; Spin scan
|
||||
(scan spin (streaming:audio-beat music t)
|
||||
:init {:angle 0 :dir 1 :speed 2}
|
||||
:step (let [new-dir (if (< (core:rand) 0.05) (* dir -1) dir)
|
||||
new-speed (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) speed)]
|
||||
(dict :angle (+ angle (* new-dir new-speed))
|
||||
:dir new-dir
|
||||
:speed new-speed)))
|
||||
|
||||
;; Ripple scan
|
||||
(scan ripple-state (streaming:audio-beat music t)
|
||||
:init {:gate 0 :cx 960 :cy 540}
|
||||
:step (let [new-gate (if (< (core:rand) 0.15) (+ 3 (core:rand-int 0 5)) (core:max 0 (- gate 1)))
|
||||
new-cx (if (> new-gate gate) (+ 200 (core:rand-int 0 1520)) cx)
|
||||
new-cy (if (> new-gate gate) (+ 200 (core:rand-int 0 680)) cy)]
|
||||
(dict :gate new-gate :cx new-cx :cy new-cy)))
|
||||
|
||||
;; Pair states
|
||||
(scan pairs (streaming:audio-beat music t)
|
||||
:init {:states (map (core:range (len sources)) (lambda (_)
|
||||
{:inv-a 0 :inv-b 0 :hue-a 0 :hue-b 0 :hue-a-val 0 :hue-b-val 0 :mix 0.5 :mix-rem 5 :angle 0 :rot-beat 0 :rot-clen 25}))}
|
||||
:step (dict :states (map states (lambda (p)
|
||||
(let [new-inv-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-a) 1)))
|
||||
new-inv-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-b) 1)))
|
||||
old-hue-a (get p :hue-a)
|
||||
old-hue-b (get p :hue-b)
|
||||
new-hue-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-a 1)))
|
||||
new-hue-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-b 1)))
|
||||
new-hue-a-val (if (> new-hue-a old-hue-a) (+ 30 (* (core:rand) 300)) (get p :hue-a-val))
|
||||
new-hue-b-val (if (> new-hue-b old-hue-b) (+ 30 (* (core:rand) 300)) (get p :hue-b-val))
|
||||
mix-rem (get p :mix-rem)
|
||||
old-mix (get p :mix)
|
||||
new-mix-rem (if (> mix-rem 0) (- mix-rem 1) (+ 1 (core:rand-int 1 10)))
|
||||
new-mix (if (> mix-rem 0) old-mix (* (core:rand-int 0 2) 0.5))
|
||||
rot-beat (get p :rot-beat)
|
||||
rot-clen (get p :rot-clen)
|
||||
old-angle (get p :angle)
|
||||
new-rot-beat (if (< (+ rot-beat 1) rot-clen) (+ rot-beat 1) 0)
|
||||
new-rot-clen (if (< (+ rot-beat 1) rot-clen) rot-clen (+ 20 (core:rand-int 0 10)))
|
||||
new-angle (+ old-angle (/ 360 rot-clen))]
|
||||
(dict :inv-a new-inv-a :inv-b new-inv-b
|
||||
:hue-a new-hue-a :hue-b new-hue-b
|
||||
:hue-a-val new-hue-a-val :hue-b-val new-hue-b-val
|
||||
:mix new-mix :mix-rem new-mix-rem
|
||||
:angle new-angle :rot-beat new-rot-beat :rot-clen new-rot-clen))))))
|
||||
|
||||
;; === OPTIMIZED PROCESS-PAIR MACRO ===
|
||||
;; Uses fused-pipeline to batch rotate+hue+invert into single kernel
|
||||
(defmacro process-pair-fast (idx)
|
||||
(let [;; Get sources for this pair (with safe modulo indexing)
|
||||
num-sources (len sources)
|
||||
src-a (nth sources (mod (* idx 2) num-sources))
|
||||
src-b (nth sources (mod (+ (* idx 2) 1) num-sources))
|
||||
cfg (nth pair-configs idx)
|
||||
pstate (nth (bind pairs :states) idx)
|
||||
|
||||
;; Read frames (GPU decode, stays on GPU)
|
||||
frame-a (streaming:source-read src-a t)
|
||||
frame-b (streaming:source-read src-b t)
|
||||
|
||||
;; Get state values
|
||||
dir (get cfg :dir)
|
||||
rot-max-a (get cfg :rot-a)
|
||||
rot-max-b (get cfg :rot-b)
|
||||
zoom-max-a (get cfg :zoom-a)
|
||||
zoom-max-b (get cfg :zoom-b)
|
||||
pair-angle (get pstate :angle)
|
||||
inv-a-on (> (get pstate :inv-a) 0)
|
||||
inv-b-on (> (get pstate :inv-b) 0)
|
||||
hue-a-on (> (get pstate :hue-a) 0)
|
||||
hue-b-on (> (get pstate :hue-b) 0)
|
||||
hue-a-val (get pstate :hue-a-val)
|
||||
hue-b-val (get pstate :hue-b-val)
|
||||
mix-ratio (get pstate :mix)
|
||||
|
||||
;; Calculate rotation angles
|
||||
angle-a (* dir pair-angle rot-max-a 0.01)
|
||||
angle-b (* dir pair-angle rot-max-b 0.01)
|
||||
|
||||
;; Energy-driven zoom (maps audio energy 0-1 to 1-max)
|
||||
zoom-a (core:map-range e 0 1 1 zoom-max-a)
|
||||
zoom-b (core:map-range e 0 1 1 zoom-max-b)
|
||||
|
||||
;; Define effect pipelines for each source
|
||||
;; These get compiled to single CUDA kernels!
|
||||
effects-a [{:op "zoom" :amount zoom-a}
|
||||
{:op "rotate" :angle angle-a}
|
||||
{:op "hue_shift" :degrees (if hue-a-on hue-a-val 0)}
|
||||
{:op "invert" :amount (if inv-a-on 1 0)}]
|
||||
effects-b [{:op "zoom" :amount zoom-b}
|
||||
{:op "rotate" :angle angle-b}
|
||||
{:op "hue_shift" :degrees (if hue-b-on hue-b-val 0)}
|
||||
{:op "invert" :amount (if inv-b-on 1 0)}]
|
||||
|
||||
;; Apply fused pipelines (single kernel per source!)
|
||||
processed-a (streaming:fused-pipeline frame-a effects-a)
|
||||
processed-b (streaming:fused-pipeline frame-b effects-b)]
|
||||
|
||||
;; Blend the two processed frames
|
||||
(blending:blend-images processed-a processed-b mix-ratio)))
|
||||
|
||||
;; === FRAME PIPELINE ===
|
||||
(frame
|
||||
(let [now t
|
||||
e (streaming:audio-energy music now)
|
||||
|
||||
;; Get cycle state
|
||||
active (bind cycle :active)
|
||||
beat-pos (bind cycle :beat)
|
||||
clen (bind cycle :clen)
|
||||
|
||||
;; Transition logic
|
||||
phase3 (* beat-pos 3)
|
||||
fading (and (>= phase3 (* clen 2)) (< phase3 (* clen 3)))
|
||||
fade-amt (if fading (/ (- phase3 (* clen 2)) clen) 0)
|
||||
next-idx (mod (+ active 1) (len sources))
|
||||
|
||||
;; Process active pair with fused pipeline
|
||||
active-frame (process-pair-fast active)
|
||||
|
||||
;; Crossfade with zoom during transition
|
||||
;; Old pair: zooms out (1.0 -> 2.0) and fades out
|
||||
;; New pair: starts small (0.1), zooms in (-> 1.0) and fades in
|
||||
result (if fading
|
||||
(let [next-frame (process-pair-fast next-idx)
|
||||
;; Active zooms out as it fades
|
||||
active-zoom (+ 1.0 fade-amt)
|
||||
active-zoomed (streaming:fused-pipeline active-frame
|
||||
[{:op "zoom" :amount active-zoom}])
|
||||
;; Next starts small and zooms in
|
||||
next-zoom (+ 0.1 (* fade-amt 0.9))
|
||||
next-zoomed (streaming:fused-pipeline next-frame
|
||||
[{:op "zoom" :amount next-zoom}])]
|
||||
(blending:blend-images active-zoomed next-zoomed fade-amt))
|
||||
active-frame)
|
||||
|
||||
;; Final effects pipeline (fused!)
|
||||
spin-angle (bind spin :angle)
|
||||
rip-gate (bind ripple-state :gate)
|
||||
rip-amp (* rip-gate (core:map-range e 0 1 5 50))
|
||||
rip-cx (bind ripple-state :cx)
|
||||
rip-cy (bind ripple-state :cy)
|
||||
|
||||
;; Fused final effects
|
||||
final-effects [{:op "rotate" :angle spin-angle}
|
||||
{:op "ripple" :amplitude rip-amp :frequency 8 :decay 2
|
||||
:phase (* now 5) :center_x rip-cx :center_y rip-cy}]]
|
||||
|
||||
;; Apply final fused pipeline
|
||||
(streaming:fused-pipeline result final-effects
|
||||
:rotate_angle spin-angle
|
||||
:ripple_phase (* now 5)
|
||||
:ripple_amplitude rip-amp))))
|
||||
134
l1/recipes/woods-recipe.sexp
Normal file
134
l1/recipes/woods-recipe.sexp
Normal file
@@ -0,0 +1,134 @@
|
||||
;; Woods Recipe - Using friendly names for all assets
|
||||
;;
|
||||
;; Requires uploaded:
|
||||
;; - Media: woods-1 through woods-8 (videos), woods-audio (audio)
|
||||
;; - Effects: fx-rotate, fx-zoom, fx-blend, fx-ripple, fx-invert, fx-hue-shift
|
||||
;; - Templates: tpl-standard-primitives, tpl-standard-effects, tpl-process-pair,
|
||||
;; tpl-crossfade-zoom, tpl-scan-spin, tpl-scan-ripple
|
||||
|
||||
(stream "woods-recipe"
|
||||
:fps 30
|
||||
:width 1920
|
||||
:height 1080
|
||||
:seed 42
|
||||
|
||||
;; Load standard primitives and effects via friendly names
|
||||
(include :name "tpl-standard-primitives")
|
||||
(include :name "tpl-standard-effects")
|
||||
|
||||
;; Load reusable templates
|
||||
(include :name "tpl-process-pair")
|
||||
(include :name "tpl-crossfade-zoom")
|
||||
|
||||
;; === SOURCES AS ARRAY (using friendly names) ===
|
||||
(def sources [
|
||||
(streaming:make-video-source "woods-1" 30)
|
||||
(streaming:make-video-source "woods-2" 30)
|
||||
(streaming:make-video-source "woods-3" 30)
|
||||
(streaming:make-video-source "woods-4" 30)
|
||||
(streaming:make-video-source "woods-5" 30)
|
||||
(streaming:make-video-source "woods-6" 30)
|
||||
(streaming:make-video-source "woods-7" 30)
|
||||
(streaming:make-video-source "woods-8" 30)
|
||||
])
|
||||
|
||||
;; Per-pair config: [rot-dir, rot-a-max, rot-b-max, zoom-a-max, zoom-b-max]
|
||||
(def pair-configs [
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir -1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
{:dir 1 :rot-a 30 :rot-b -30 :zoom-a 1.3 :zoom-b 0.7}
|
||||
{:dir -1 :rot-a -45 :rot-b 45 :zoom-a 0.5 :zoom-b 1.5}
|
||||
{:dir 1 :rot-a 45 :rot-b -45 :zoom-a 1.5 :zoom-b 0.5}
|
||||
])
|
||||
|
||||
;; Audio analyzer (using friendly name)
|
||||
(def music (streaming:make-audio-analyzer "woods-audio"))
|
||||
|
||||
;; Audio playback (friendly name resolved by streaming primitives)
|
||||
(audio-playback "woods-audio")
|
||||
|
||||
;; === GLOBAL SCANS ===
|
||||
|
||||
;; Cycle state: which source is active
|
||||
(scan cycle (streaming:audio-beat music t)
|
||||
:init {:active 0 :beat 0 :clen 16}
|
||||
:step (if (< (+ beat 1) clen)
|
||||
(dict :active active :beat (+ beat 1) :clen clen)
|
||||
(dict :active (mod (+ active 1) (len sources)) :beat 0
|
||||
:clen (+ 8 (mod (* (streaming:audio-beat-count music t) 7) 17)))))
|
||||
|
||||
;; Reusable scans from templates
|
||||
(include :name "tpl-scan-spin")
|
||||
(include :name "tpl-scan-ripple")
|
||||
|
||||
;; === PER-PAIR STATE ===
|
||||
(scan pairs (streaming:audio-beat music t)
|
||||
:init {:states (map (core:range (len sources)) (lambda (_)
|
||||
{:inv-a 0 :inv-b 0 :hue-a 0 :hue-b 0 :hue-a-val 0 :hue-b-val 0 :mix 0.5 :mix-rem 5 :angle 0 :rot-beat 0 :rot-clen 25}))}
|
||||
:step (dict :states (map states (lambda (p)
|
||||
(let [new-inv-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-a) 1)))
|
||||
new-inv-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- (get p :inv-b) 1)))
|
||||
old-hue-a (get p :hue-a)
|
||||
old-hue-b (get p :hue-b)
|
||||
new-hue-a (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-a 1)))
|
||||
new-hue-b (if (< (core:rand) 0.1) (+ 1 (core:rand-int 1 4)) (core:max 0 (- old-hue-b 1)))
|
||||
new-hue-a-val (if (> new-hue-a old-hue-a) (+ 30 (* (core:rand) 300)) (get p :hue-a-val))
|
||||
new-hue-b-val (if (> new-hue-b old-hue-b) (+ 30 (* (core:rand) 300)) (get p :hue-b-val))
|
||||
mix-rem (get p :mix-rem)
|
||||
old-mix (get p :mix)
|
||||
new-mix-rem (if (> mix-rem 0) (- mix-rem 1) (+ 1 (core:rand-int 1 10)))
|
||||
new-mix (if (> mix-rem 0) old-mix (* (core:rand-int 0 2) 0.5))
|
||||
rot-beat (get p :rot-beat)
|
||||
rot-clen (get p :rot-clen)
|
||||
old-angle (get p :angle)
|
||||
new-rot-beat (if (< (+ rot-beat 1) rot-clen) (+ rot-beat 1) 0)
|
||||
new-rot-clen (if (< (+ rot-beat 1) rot-clen) rot-clen (+ 20 (core:rand-int 0 10)))
|
||||
new-angle (+ old-angle (/ 360 rot-clen))]
|
||||
(dict :inv-a new-inv-a :inv-b new-inv-b
|
||||
:hue-a new-hue-a :hue-b new-hue-b
|
||||
:hue-a-val new-hue-a-val :hue-b-val new-hue-b-val
|
||||
:mix new-mix :mix-rem new-mix-rem
|
||||
:angle new-angle :rot-beat new-rot-beat :rot-clen new-rot-clen))))))
|
||||
|
||||
;; === FRAME PIPELINE ===
|
||||
(frame
|
||||
(let [now t
|
||||
e (streaming:audio-energy music now)
|
||||
|
||||
;; Get cycle state
|
||||
active (bind cycle :active)
|
||||
beat-pos (bind cycle :beat)
|
||||
clen (bind cycle :clen)
|
||||
|
||||
;; Transition logic
|
||||
phase3 (* beat-pos 3)
|
||||
fading (and (>= phase3 (* clen 2)) (< phase3 (* clen 3)))
|
||||
fade-amt (if fading (/ (- phase3 (* clen 2)) clen) 0)
|
||||
next-idx (mod (+ active 1) (len sources))
|
||||
|
||||
;; Get pair states array
|
||||
pair-states (bind pairs :states)
|
||||
|
||||
;; Process active pair using macro from template
|
||||
active-frame (process-pair active)
|
||||
|
||||
;; Crossfade with zoom during transition
|
||||
result (if fading
|
||||
(crossfade-zoom active-frame (process-pair next-idx) fade-amt)
|
||||
active-frame)
|
||||
|
||||
;; Final: global spin + ripple
|
||||
spun (rotate result :angle (bind spin :angle))
|
||||
rip-gate (bind ripple-state :gate)
|
||||
rip-amp (* rip-gate (core:map-range e 0 1 5 50))]
|
||||
|
||||
(ripple spun
|
||||
:amplitude rip-amp
|
||||
:center_x (bind ripple-state :cx)
|
||||
:center_y (bind ripple-state :cy)
|
||||
:frequency 8
|
||||
:decay 2
|
||||
:speed 5))))
|
||||
Reference in New Issue
Block a user