view clojure/com/aurellem/run/final_cut.clj @ 547:b3b7a06686c4

all synching issues resolved.
author Robert McIntyre <rlm@mit.edu>
date Wed, 27 Jun 2012 19:38:13 -0500
parents 36e5fa62eb3c
children 6ce1d5fd4911
line wrap: on
line source
1 (ns com.aurellem.run.final-cut
2 (:use (com.aurellem.gb saves gb-driver util constants
3 items vbm characters money
4 rlm-assembly))
5 (:use (com.aurellem.run util sound music title save-corruption
6 bootstrap-0 bootstrap-1
7 ram-display
8 ))
9 (:require clojure.string)
10 (:import [com.aurellem.gb.gb_driver SaveState])
11 (:import java.awt.image.BufferedImage)
12 (:import java.io.File)
13 (:import com.aurellem.gb.WaveWriter))
16 (def render-dir (File. user-home "proj/vba-clojure/render/test"))
18 (defn write-move-image! [move target-file]
19 (let [actual-keys (set move)
20 keys [:a :b :start :select :u :d :l :r]
21 button-on-name ["-size" "5x5" "xc:red"]
22 button-off-name ["-size" "5x5" "xc:white"]]
23 (apply
24 clojure.java.shell/sh
25 (flatten
26 ["convert"
27 "-size" "64x5" "xc:white"
28 (map
29 (fn [index]
30 (let [position ["-geometry"
31 (str "+" (* index 8)
32 "+" "0") "-composite"]]
33 (if (actual-keys (keys index))
34 [button-on-name position]
35 [button-off-name position])))
36 (range (count keys)))
37 (.getCanonicalPath target-file)]))))
39 (defn render-files!
40 ([^File target-dir initial-state moves]
41 (let [ram-map-dir (File. target-dir "ram-map")
42 frames-dir (File. target-dir "frames")
43 moves-dir (File. target-dir "moves")
44 audio-file (File. target-dir "audio.wav")
45 _ (.mkdir target-dir)
46 _ (.mkdir ram-map-dir)
47 _ (.mkdir frames-dir)
48 _ (.mkdir moves-dir)
49 wave-writer (WaveWriter. audio-file)
50 moves (vec moves)
51 desired-fps 60
52 seconds-per-frame (/ 1.0 desired-fps)]
53 (set-state! initial-state)
54 ;; clear sound buffer
55 (sound-bytes)
56 (try
57 (dorun
58 (reduce
59 (fn [[total-audio-samples
60 total-video-samples] move]
61 (run-moves @current-state (vector move))
62 (let [sound (sound-bytes)
63 total-audio-samples (+ total-audio-samples
64 (count sound))
65 total-audio-time
66 (*
67 (/ total-audio-samples 4)
68 (/ 44100))
70 total-video-time (* total-video-samples
71 seconds-per-frame)
73 av-diff (- total-audio-time
74 total-video-time)
75 write-video!
76 (fn [index]
77 ;; write screenshot
78 (write-move-image!
79 move
80 (File. moves-dir (format "%07d.png" index)))
82 (write-png!
83 @current-state
84 (File. frames-dir (format "%07d.png" index)))
86 ;; write ram-image
87 (write-ram-image!
88 @current-state
89 (File. ram-map-dir (format "%07d.png" index))))]
90 ;;(println "audio-samples:" (count sound))
92 ;; record audio
93 (.process wave-writer sound gb-sound-format)
95 ;; duplicate or drop frames depending on
96 ;; desired-fps
97 (if (> (Math/abs av-diff) (* 2 seconds-per-frame))
98 (if (< 0 av-diff)
99 ;; audio has gone past video, duplicate video.
100 (do (println
101 "duplicating frame, av-diff is" av-diff)
102 (write-video! (+ 1 total-video-samples))
103 (write-video! (+ 2 total-video-samples))
104 [total-audio-samples
105 (+ 2 total-video-samples)])
106 ;; video has gone past audio, drop frame.
107 (do (println
108 "dropping frame, av-diff is" av-diff)
109 [total-audio-samples
110 total-video-samples]))
111 ;; no frame dropping or duplication required.
112 (do ;;(println "all normal")
113 (write-video! (+ 1 total-video-samples))
114 [total-audio-samples
115 (+ 1 total-video-samples)]))))
116 [0 0 0] moves))
117 (finally
118 (do
119 (println "cleanup audio.")
120 (.cleanup wave-writer))))))
121 ([initial-state moves]
122 (render-files! render-dir initial-state moves))
123 ([moves]
124 (render-files! (root) moves)))
126 (defn file-names [#^File dir]
127 (mapv #(.getCanonicalPath %) (next (sort (file-seq dir)))))
129 (defn composite-frames-command
130 [move-image screenshot ram-map target]
131 ["convert"
132 "-size" "318x276" "xc:white"
133 move-image "-geometry" "+30+225" "-composite"
134 screenshot "-geometry" "+10+10" "-composite"
135 ram-map "-geometry" "+180+10" "-composite"
136 target])
138 (defn generate-composite-frames!
139 [^File rendered-dir]
140 (let [final-frames (File. rendered-dir "final")
141 _ (.mkdir final-frames)
142 move-images (file-names (File. rendered-dir "moves"))
143 screenshots (file-names (File. rendered-dir "frames"))
144 ram-maps (file-names (File. rendered-dir "ram-map"))
145 targets (map #(.getCanonicalPath
146 (File. final-frames (format "%07d.bmp" %)))
147 (range (count screenshots)))]
148 (dorun
149 (map
150 (comp
151 (partial apply clojure.java.shell/sh)
152 flatten
153 (partial apply composite-frames-command)
154 (fn [a b c d] (println d) [a b c d]))
155 move-images screenshots ram-maps targets))))
158 (defn final-cut! [^File render-dir]
159 (let [movie (File. render-dir "final.ogg")
160 final-audio (File. render-dir "final.wav")
161 final (File. render-dir "final")]
162 (.delete movie)
163 (.delete final-audio)
164 (clojure.java.shell/sh
165 "sox" (.getCanonicalPath (File. render-dir "audio.wav"))
166 (.getCanonicalPath final-audio))
167 (clojure.java.shell/sh
168 "ffmpeg" "-r" "60" ;; maybe 59.7 ???!
169 "-i" (str (.getCanonicalPath final) "/" "%07d.bmp")
170 "-i" (.getCanonicalPath final-audio)
171 "-b:a" "128k"
172 "-b:v" "9000k"
173 "-c:a" "libvorbis"
174 "-f" "webm"
175 "-g" "200"
176 (.getCanonicalPath movie)) nil))
179 (comment
181 ;; step 1
183 (render-files!
184 render-dir (root)
185 (take 9000 (first (control-checkpoint))))
187 ;; step 2
189 (generate-composite-frames!
190 render-dir
191 (take 9000 (first (control-checkpoint))))
193 ;; step 3
194 (final-cut! render-dir)