rlm@530: (ns com.aurellem.run.final-cut rlm@530: (:use (com.aurellem.gb saves gb-driver util constants rlm@530: items vbm characters money rlm@530: rlm-assembly)) rlm@530: (:use (com.aurellem.run util sound music title save-corruption rlm@530: bootstrap-0 bootstrap-1 rlm@530: ram-display rlm@530: )) rlm@530: (:require clojure.string) rlm@530: (:import [com.aurellem.gb.gb_driver SaveState]) rlm@530: (:import java.awt.image.BufferedImage) rlm@530: (:import java.io.File) rlm@530: (:import com.aurellem.gb.WaveWriter)) rlm@530: rlm@530: rlm@542: (def render-dir (File. user-home "proj/vba-clojure/render/test")) rlm@530: rlm@546: (defn write-move-image! [move target-file] rlm@546: (let [actual-keys (set move) rlm@546: keys [:a :b :start :select :u :d :l :r] rlm@546: button-on-name ["-size" "5x5" "xc:red"] rlm@546: button-off-name ["-size" "5x5" "xc:white"]] rlm@546: (apply rlm@546: clojure.java.shell/sh rlm@546: (flatten rlm@546: ["convert" rlm@546: "-size" "64x5" "xc:white" rlm@546: (map rlm@546: (fn [index] rlm@546: (let [position ["-geometry" rlm@546: (str "+" (* index 8) rlm@546: "+" "0") "-composite"]] rlm@546: (if (actual-keys (keys index)) rlm@546: [button-on-name position] rlm@546: [button-off-name position]))) rlm@546: (range (count keys))) rlm@546: (.getCanonicalPath target-file)])))) rlm@544: rlm@530: (defn render-files! rlm@530: ([^File target-dir initial-state moves] rlm@530: (let [ram-map-dir (File. target-dir "ram-map") rlm@530: frames-dir (File. target-dir "frames") rlm@546: moves-dir (File. target-dir "moves") rlm@530: audio-file (File. target-dir "audio.wav") rlm@533: _ (.mkdir target-dir) rlm@533: _ (.mkdir ram-map-dir) rlm@533: _ (.mkdir frames-dir) rlm@546: _ (.mkdir moves-dir) rlm@530: wave-writer (WaveWriter. audio-file) rlm@544: moves (vec moves) rlm@544: desired-fps 60 rlm@547: seconds-per-frame (/ 1.0 desired-fps)] rlm@530: (set-state! initial-state) rlm@544: ;; clear sound buffer rlm@544: (sound-bytes) rlm@544: (try rlm@544: (dorun rlm@544: (reduce rlm@544: (fn [[total-audio-samples rlm@544: total-video-samples] move] rlm@544: (run-moves @current-state (vector move)) rlm@544: (let [sound (sound-bytes) rlm@544: total-audio-samples (+ total-audio-samples rlm@544: (count sound)) rlm@544: total-audio-time rlm@544: (* rlm@544: (/ total-audio-samples 4) rlm@544: (/ 44100)) rlm@544: rlm@544: total-video-time (* total-video-samples rlm@544: seconds-per-frame) rlm@530: rlm@544: av-diff (- total-audio-time rlm@544: total-video-time) rlm@544: write-video! rlm@544: (fn [index] rlm@544: ;; write screenshot rlm@546: (write-move-image! rlm@546: move rlm@546: (File. moves-dir (format "%07d.png" index))) rlm@544: rlm@544: (write-png! rlm@544: @current-state rlm@544: (File. frames-dir (format "%07d.png" index))) rlm@544: rlm@544: ;; write ram-image rlm@544: (write-ram-image! rlm@544: @current-state rlm@544: (File. ram-map-dir (format "%07d.png" index))))] rlm@544: ;;(println "audio-samples:" (count sound)) rlm@544: rlm@544: ;; record audio rlm@544: (.process wave-writer sound gb-sound-format) rlm@544: rlm@544: ;; duplicate or drop frames depending on rlm@544: ;; desired-fps rlm@544: (if (> (Math/abs av-diff) (* 2 seconds-per-frame)) rlm@544: (if (< 0 av-diff) rlm@544: ;; audio has gone past video, duplicate video. rlm@544: (do (println rlm@544: "duplicating frame, av-diff is" av-diff) rlm@544: (write-video! (+ 1 total-video-samples)) rlm@544: (write-video! (+ 2 total-video-samples)) rlm@544: [total-audio-samples rlm@544: (+ 2 total-video-samples)]) rlm@544: ;; video has gone past audio, drop frame. rlm@544: (do (println rlm@544: "dropping frame, av-diff is" av-diff) rlm@544: [total-audio-samples rlm@544: total-video-samples])) rlm@544: ;; no frame dropping or duplication required. rlm@544: (do ;;(println "all normal") rlm@544: (write-video! (+ 1 total-video-samples)) rlm@544: [total-audio-samples rlm@544: (+ 1 total-video-samples)])))) rlm@544: [0 0 0] moves)) rlm@544: (finally rlm@544: (do rlm@544: (println "cleanup audio.") rlm@544: (.cleanup wave-writer)))))) rlm@544: ([initial-state moves] rlm@544: (render-files! render-dir initial-state moves)) rlm@544: ([moves] rlm@544: (render-files! (root) moves))) rlm@530: rlm@531: (defn file-names [#^File dir] rlm@531: (mapv #(.getCanonicalPath %) (next (sort (file-seq dir))))) rlm@531: rlm@531: (defn composite-frames-command rlm@546: [move-image screenshot ram-map target] rlm@546: ["convert" rlm@546: "-size" "318x276" "xc:white" rlm@546: move-image "-geometry" "+30+225" "-composite" rlm@546: screenshot "-geometry" "+10+10" "-composite" rlm@546: ram-map "-geometry" "+180+10" "-composite" rlm@546: target]) rlm@531: rlm@531: (defn generate-composite-frames! rlm@547: [^File rendered-dir] rlm@532: (let [final-frames (File. rendered-dir "final") rlm@532: _ (.mkdir final-frames) rlm@547: move-images (file-names (File. rendered-dir "moves")) rlm@532: screenshots (file-names (File. rendered-dir "frames")) rlm@532: ram-maps (file-names (File. rendered-dir "ram-map")) rlm@532: targets (map #(.getCanonicalPath rlm@532: (File. final-frames (format "%07d.bmp" %))) rlm@532: (range (count screenshots)))] rlm@532: (dorun rlm@532: (map rlm@532: (comp rlm@532: (partial apply clojure.java.shell/sh) rlm@532: flatten rlm@532: (partial apply composite-frames-command) rlm@547: (fn [a b c d] (println d) [a b c d])) rlm@547: move-images screenshots ram-maps targets)))) rlm@531: rlm@532: rlm@549: ;; (defn final-cut! [^File render-dir] rlm@549: ;; (let [movie (File. render-dir "final.ogg") rlm@549: ;; final-audio (File. render-dir "final.wav") rlm@549: ;; final (File. render-dir "final")] rlm@549: ;; (.delete movie) rlm@549: ;; (.delete final-audio) rlm@549: ;; (clojure.java.shell/sh rlm@549: ;; "sox" (.getCanonicalPath (File. render-dir "audio.wav")) rlm@549: ;; (.getCanonicalPath final-audio)) rlm@549: ;; (clojure.java.shell/sh rlm@549: ;; "ffmpeg" "-r" "60" ;; maybe 59.7 ???! rlm@549: ;; "-i" (str (.getCanonicalPath final) "/" "%07d.bmp") rlm@549: ;; "-i" (.getCanonicalPath final-audio) rlm@549: ;; "-b:a" "128k" rlm@549: ;; "-b:v" "9000k" rlm@549: ;; "-c:a" "libvorbis" rlm@549: ;; "-f" "webm" rlm@549: ;; "-g" "200" rlm@549: ;; (.getCanonicalPath movie)) nil)) rlm@549: rlm@532: (defn final-cut! [^File render-dir] rlm@532: (let [movie (File. render-dir "final.ogg") rlm@544: final-audio (File. render-dir "final.wav") rlm@532: final (File. render-dir "final")] rlm@532: (.delete movie) rlm@547: (.delete final-audio) rlm@532: (clojure.java.shell/sh rlm@544: "sox" (.getCanonicalPath (File. render-dir "audio.wav")) rlm@544: (.getCanonicalPath final-audio)) rlm@544: (clojure.java.shell/sh rlm@549: "ffmpeg" rlm@549: "-framerate" "60" rlm@532: "-i" (str (.getCanonicalPath final) "/" "%07d.bmp") rlm@544: "-i" (.getCanonicalPath final-audio) rlm@532: "-b:a" "128k" rlm@532: "-b:v" "9000k" rlm@532: "-c:a" "libvorbis" rlm@549: "-c:v" "libtheora" rlm@549: "-r" "60" rlm@532: (.getCanonicalPath movie)) nil)) rlm@549: rlm@531: rlm@532: (comment rlm@531: rlm@532: ;; step 1 rlm@532: rlm@532: (render-files! rlm@532: render-dir (root) rlm@532: (take 9000 (first (control-checkpoint)))) rlm@531: rlm@532: ;; step 2 rlm@532: rlm@532: (generate-composite-frames! rlm@532: render-dir rlm@532: (take 9000 (first (control-checkpoint)))) rlm@532: rlm@532: ;; step 3 rlm@532: (final-cut! render-dir) rlm@532: )