view clojure/com/aurellem/run/final_cut.clj @ 545:2b6cdea9bcdb

audio now syncs properly, but moves no longer sync with main display.
author Robert McIntyre <rlm@mit.edu>
date Wed, 27 Jun 2012 13:41:01 -0500
parents 86d23b6d433f
children 36e5fa62eb3c
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 force-sync
19 "keep the entire movie to a certain fps"
20 [fps]
23 )
26 (defn render-files!
27 ([^File target-dir initial-state moves]
28 (let [ram-map-dir (File. target-dir "ram-map")
29 frames-dir (File. target-dir "frames")
30 audio-file (File. target-dir "audio.wav")
31 _ (.mkdir target-dir)
32 _ (.mkdir ram-map-dir)
33 _ (.mkdir frames-dir)
34 wave-writer (WaveWriter. audio-file)
35 moves (vec moves)
36 desired-fps 60
37 seconds-per-frame (/ 1.0 desired-fps)
39 ]
40 (set-state! initial-state)
41 ;; clear sound buffer
42 (sound-bytes)
43 (try
44 (dorun
45 (reduce
46 (fn [[total-audio-samples
47 total-video-samples] move]
48 (run-moves @current-state (vector move))
49 (let [sound (sound-bytes)
50 total-audio-samples (+ total-audio-samples
51 (count sound))
52 total-audio-time
53 (*
54 (/ total-audio-samples 4)
55 (/ 44100))
57 total-video-time (* total-video-samples
58 seconds-per-frame)
60 av-diff (- total-audio-time
61 total-video-time)
62 write-video!
63 (fn [index]
64 ;; write screenshot
67 (write-png!
68 @current-state
69 (File. frames-dir (format "%07d.png" index)))
71 ;; write ram-image
72 (write-ram-image!
73 @current-state
74 (File. ram-map-dir (format "%07d.png" index))))]
75 ;;(println "audio-samples:" (count sound))
77 ;; record audio
78 (.process wave-writer sound gb-sound-format)
80 ;; duplicate or drop frames depending on
81 ;; desired-fps
82 (if (> (Math/abs av-diff) (* 2 seconds-per-frame))
83 (if (< 0 av-diff)
84 ;; audio has gone past video, duplicate video.
85 (do (println
86 "duplicating frame, av-diff is" av-diff)
87 (write-video! (+ 1 total-video-samples))
88 (write-video! (+ 2 total-video-samples))
89 [total-audio-samples
90 (+ 2 total-video-samples)])
91 ;; video has gone past audio, drop frame.
92 (do (println
93 "dropping frame, av-diff is" av-diff)
94 [total-audio-samples
95 total-video-samples]))
96 ;; no frame dropping or duplication required.
97 (do ;;(println "all normal")
98 (write-video! (+ 1 total-video-samples))
99 [total-audio-samples
100 (+ 1 total-video-samples)]))))
101 [0 0 0] moves))
102 (finally
103 (do
104 (println "cleanup audio.")
105 (.cleanup wave-writer))))))
106 ([initial-state moves]
107 (render-files! render-dir initial-state moves))
108 ([moves]
109 (render-files! (root) moves)))
111 (defn file-names [#^File dir]
112 (mapv #(.getCanonicalPath %) (next (sort (file-seq dir)))))
114 (defn composite-frames-command
115 [screenshot ram-map target move]
116 (let [actual-keys (set move)
117 keys [:a :b :start :select :u :d :l :r]
118 button-on-name ["-size" "5x5" "xc:red"]
119 button-off-name ["-size" "5x5" "xc:white"]]
120 ["convert"
121 "-size" "318x276" "xc:white"
122 (map
123 (fn [index]
124 (let [position ["-geometry"
125 (str "+" (+ 30 (* index 8))
126 "+" "225") "-composite"]]
127 (if (actual-keys (keys index))
128 [button-on-name position]
129 [button-off-name position])))
130 (range (count keys)))
131 screenshot "-geometry" "+10+10" "-composite"
132 ram-map "-geometry" "+180+10" "-composite"
133 target]))
135 (defn generate-composite-frames!
136 [^File rendered-dir moves]
137 (let [final-frames (File. rendered-dir "final")
138 _ (.mkdir final-frames)
139 screenshots (file-names (File. rendered-dir "frames"))
140 ram-maps (file-names (File. rendered-dir "ram-map"))
141 targets (map #(.getCanonicalPath
142 (File. final-frames (format "%07d.bmp" %)))
143 (range (count screenshots)))]
144 (dorun
145 (map
146 (comp
147 (partial apply clojure.java.shell/sh)
148 flatten
149 (partial apply composite-frames-command)
150 (fn [a b c d] (println c) [a b c d]))
151 screenshots ram-maps targets moves))))
154 (defn final-cut! [^File render-dir]
155 (let [movie (File. render-dir "final.ogg")
156 final-audio (File. render-dir "final.wav")
157 final (File. render-dir "final")]
158 (.delete movie)
159 (clojure.java.shell/sh
160 "sox" (.getCanonicalPath (File. render-dir "audio.wav"))
161 (.getCanonicalPath final-audio))
162 (clojure.java.shell/sh
163 "ffmpeg" "-r" "60" ;; maybe 59.7 ???!
164 "-i" (str (.getCanonicalPath final) "/" "%07d.bmp")
165 "-i" (.getCanonicalPath final-audio)
166 "-b:a" "128k"
167 "-b:v" "9000k"
168 "-c:a" "libvorbis"
169 "-c:v" "libtheora"
170 (.getCanonicalPath movie)) nil))
173 (comment
175 ;; step 1
177 (render-files!
178 render-dir (root)
179 (take 9000 (first (control-checkpoint))))
181 ;; step 2
183 (generate-composite-frames!
184 render-dir
185 (take 9000 (first (control-checkpoint))))
187 ;; step 3
188 (final-cut! render-dir)