;; slice-every-n construct - group every N beats into one segment ;; ;; Usage: ;; (construct slice-every-n :path "constructs/slice-every-n.sexp") ;; (def segments (slice-every-n beats-data 4 ;; :init 0 ;; :reducer (fn [acc i start end] ;; {:source video-a ;; :effects (list {:effect invert}) ;; :acc (inc acc)}))) ;; ;; Groups every N analysis times into one segment, calling reducer once per group (define-construct slice-every-n "Group every N analysis beats into segments" (analysis n) ;; 'init' and 'reducer' come from keyword args ;; Reducer receives: (acc, i, start, end) where start/end are audio beat times ;; Reducer returns: {:source src :effects fx :acc new-acc} ;; Optionally include :start/:end to override (e.g., for wrapping/randomizing) ;; :duration is calculated from start/end (use :duration to override) ;; Return :skip true to skip this segment (let [times (get analysis :times) ;; Group times into chunks of n grouped (chunk-every times n)] (nth (reduce (fn [state group] (let [acc (first state) segments (nth state 1) i (len segments) audio-start (first group) audio-end (last group) audio-duration (- audio-end audio-start) ;; Call user's reducer with audio beat times result (reducer acc i audio-start audio-end) new-acc (get result :acc)] ;; Skip if reducer returns :skip true (if (get result :skip false) (list new-acc segments) (let [;; Use reducer's start/end/duration if provided, else use audio times seg-start (get result :start audio-start) seg-end (get result :end audio-end) seg-duration (get result :duration (- seg-end seg-start)) segment (dict :source (get result :source) :start seg-start :end seg-end :duration seg-duration :effects (get result :effects))] (list new-acc (append segments segment)))))) (list init (list)) grouped) 1)))