Mercurial > vba-clojure
diff clojure/com/aurellem/run/music.clj @ 484:f6b5a1914efa
merge.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 06 May 2012 20:52:31 -0500 |
parents | 221b3fea9221 |
children | 346b91ae503a |
line wrap: on
line diff
1.1 --- a/clojure/com/aurellem/run/music.clj Wed May 02 13:07:36 2012 -0500 1.2 +++ b/clojure/com/aurellem/run/music.clj Sun May 06 20:52:31 2012 -0500 1.3 @@ -8,15 +8,42 @@ 1.4 (:import [com.aurellem.gb.gb_driver SaveState]) 1.5 (:import java.io.File)) 1.6 1.7 -(def third-kind 1.8 - (File. "/home/r/proj/midi/third-kind.mid")) 1.9 +(def pony 1.10 + (File. user-home "/proj/vba-clojure/music/pony-title.mid")) 1.11 + 1.12 +(def pony-csv 1.13 + (File. user-home "proj/vba-clojure/music/pony-title.csv")) 1.14 + 1.15 +(def sync-test 1.16 + (File. user-home "proj/vba-clojure/music/sync-test.mid")) 1.17 + 1.18 +(def drum-test 1.19 + (File. user-home "proj/vba-clojure/music/drum-test.mid")) 1.20 + 1.21 +(def regret 1.22 + (File. user-home "proj/vba-clojure/music/ship-of-regret-and-sleep.mid")) 1.23 + 1.24 +(def regret-csv 1.25 + (File. user-home "proj/vba-clojure/music/ship-of-regret-and-sleep.csv")) 1.26 + 1.27 +(def mother 1.28 + (File. user-home "proj/vba-clojure/music/mother.mid")) 1.29 + 1.30 +(def mother-csv 1.31 + (File. user-home "proj/vba-clojure/music/mother.csv")) 1.32 + 1.33 1.34 (defn raw-midi-text [#^File midi-file] 1.35 - (:out 1.36 - (clojure.java.shell/sh 1.37 - "midicsv" 1.38 - (.getCanonicalPath midi-file) 1.39 - "-"))) 1.40 + (let [extention (apply str (take-last 3 (.getCanonicalPath 1.41 + midi-file)))] 1.42 + (cond (= "mid" extention) 1.43 + (:out 1.44 + (clojure.java.shell/sh 1.45 + "midicsv" 1.46 + (.getCanonicalPath midi-file) 1.47 + "-")) 1.48 + (= "csv" extention) 1.49 + (slurp midi-file)))) 1.50 1.51 (def command-line #"^(\d+), (\d+), ([^,]+)(.*)$") 1.52 1.53 @@ -170,8 +197,9 @@ 1.54 "Read the message which starts at the current value of HL and do 1.55 what it says. Duration is left in A, and HL is advanced 1.56 appropraitely." 1.57 - ([] (do-message 0x16)) 1.58 - ([sound-base-address] 1.59 + ([] (do-message 0x16 1)) 1.60 + ([sound-base-address wave-duty] 1.61 + (assert (<= 0 wave-duty 3)) 1.62 (let [switch 1.63 [0x2A ;; load message code into A, increment HL 1.64 1.65 @@ -183,7 +211,11 @@ 1.66 :note-length] 1.67 1.68 play-note 1.69 - [0x2A ;; load volume/frequency-high info 1.70 + [0x3E ;; set wave-duty 1.71 + (bit-shift-left wave-duty 6) 1.72 + 0xE0 1.73 + sound-base-address 1.74 + 0x2A ;; load volume/frequency-high info 1.75 0xF5 ;; push A 1.76 0xE6 1.77 (Integer/parseInt "11110000" 2) ;; volume mask 1.78 @@ -203,6 +235,22 @@ 1.79 {:note-length (count play-note)} 1.80 (concat switch play-note))))) 1.81 1.82 +(defn play-noise 1.83 + "read [noise-code, volume, duration] and play the noise. Duration is left in 1.84 + A, and HL is advanced appropraitely." 1.85 + ([] 1.86 + [0x2A ;; load noise-code into A 1.87 + 0xE0 1.88 + 0x22 ;; write noise-code 1.89 + 1.90 + 0x2A ;; load volume 1.91 + 0xE0 1.92 + 0x21 ;; write volume 1.93 + 1.94 + 0x2A] ;; load duration into A 1.95 + )) 1.96 + 1.97 + 1.98 ;; (defn play-note 1.99 ;; "Play the note referenced by HL in the appropiate channel. 1.100 ;; Leaves desired-duration in A." 1.101 @@ -226,7 +274,7 @@ 1.102 ;; 0x2A ;; load duration 1.103 ;; ]) 1.104 1.105 -(defn music-step [sound-base-address] 1.106 +(defn music-step [sound-base-address wave-duty noise?] 1.107 ;; C == current-ticks 1.108 ;; A == desired-ticks 1.109 1.110 @@ -254,10 +302,14 @@ 1.111 ;; if desired-ticks = current ticks 1.112 ;; go to next note ; set current set ticks to 0. 1.113 1.114 - 0x20 1.115 - (+ (count (do-message 0)) 2) 1.116 - 1.117 - (do-message sound-base-address) 1.118 + (if noise? 1.119 + [0x20 1.120 + (+ 2 (count (play-noise))) 1.121 + (play-noise)] 1.122 + 1.123 + [0x20 1.124 + (+ (count (do-message 0 0)) 2) 1.125 + (do-message sound-base-address wave-duty)]) 1.126 1.127 0x0E 1.128 0x00 ;; 0->C (current-ticks) 1.129 @@ -273,7 +325,7 @@ 1.130 (def music-1 0x11) 1.131 (def music-2 0x16) 1.132 1.133 -(defn music-kernel [] 1.134 +(defn music-kernel [wave-duty-1 wave-duty-2] 1.135 (flatten 1.136 [;; global initilization section 1.137 (clear-music-registers) 1.138 @@ -313,26 +365,38 @@ 1.139 0xE5 ;; push HL 1.140 1.141 1.142 + ;; initialize frame 3 (noise) 1.143 + 0x21 1.144 + 0x00 1.145 + 0xA9 ;; 0xA9OO -> HL 1.146 + 1.147 + 0xF5 ;; push AF 1.148 + 0xC5 ;; push CB 1.149 + 0xE5 ;; push HL 1.150 + 1.151 ;; main music loop 1.152 1.153 - 0xE8 ;; SP + 6; activate frame 1 1.154 - 6 1.155 - (music-step music-1) 1.156 - ;;(repeat (count (music-step music-1)) 0x00) 1.157 + 0xE8 ;; SP + 12; activate frame 1 1.158 + 12 1.159 + (music-step music-1 wave-duty-1 false) 1.160 1.161 0xE8 ;; SP - 6; activate frame 2 1.162 (->signed-8-bit -6) 1.163 - ;;(repeat (count (music-step music-2)) 0x00) 1.164 - (music-step music-2) 1.165 - 1.166 + (music-step music-2 wave-duty-2 false) 1.167 + 1.168 + 0xE8 ;; SP - 6; activate frame 3 1.169 + (->signed-8-bit -6) 1.170 + (music-step nil nil true) 1.171 1.172 0x18 1.173 (->signed-8-bit (+ 1.174 ;; two music-steps 1.175 - (- (* 2 (count (music-step 0)))) 1.176 + (- (* 2 (count (music-step 0 0 false)))) 1.177 + (- (count (music-step nil nil true))) 1.178 -2 ;; this jump instruction 1.179 -2 ;; activate frame 1 1.180 -2 ;; activate frame 2 1.181 + -2 ;; activate frame 3 1.182 ))])) 1.183 1.184 (defn frequency-code->frequency 1.185 @@ -395,15 +459,35 @@ 1.186 [command s] 1.187 (filter #(= command (:command %)) s)) 1.188 1.189 -(defn midi-track->mini-midi [#^File midi-file track-num] 1.190 - (let [midi-events (parse-midi midi-file) 1.191 +(defn track-info [#^File midi-file] 1.192 + (let [events (parse-midi midi-file) 1.193 + track-titles (commands :Title_t events) 1.194 + track-info 1.195 + (map #(read-string (read-string (:args %))) track-titles) 1.196 + track-map 1.197 + (zipmap track-info track-titles)] 1.198 + track-map)) 1.199 + 1.200 +(defn target-tracks 1.201 + "return the track-numbers in the form [voice-0 voice-1 noise]" 1.202 + [#^File midi-file] 1.203 + (let [track-data (track-info midi-file) 1.204 + track-order 1.205 + (zipmap (map :out (keys track-data)) 1.206 + (vals track-data)) 1.207 + channel-nums (map (comp :channel track-order) (range 3))] 1.208 + channel-nums)) 1.209 + 1.210 +(defn midi-track->abstract-mini-midi 1.211 + [#^File midi-file track-num] 1.212 + (let [midi-events (parse-midi midi-file) 1.213 1.214 note-on-events (commands :Note_on_c midi-events) 1.215 note-off-events (commands :Note_off_c midi-events) 1.216 1.217 select-channel 1.218 (fn [n s] 1.219 - (sort-by :time (filter #(= n (:channel (:args %))) s))) 1.220 + (sort-by :time (filter #(= n (:channel %)) s))) 1.221 1.222 channel-on (select-channel track-num note-on-events) 1.223 1.224 @@ -418,6 +502,7 @@ 1.225 (map 1.226 (fn [note-on note-off] 1.227 {:frequency (midi-code->frequency (:note (:args note-on))) 1.228 + :midi-code (:note (:args note-on)) 1.229 :duration 1.230 (/ (* (/ tempo division) 1.231 (- (:time note-off) (:time note-on))) 1.232 @@ -439,36 +524,116 @@ 1.233 notes) 1.234 1.235 notes-with-silence 1.236 - (filter (comp not zero? :duration) 1.237 - (interleave silences notes))] 1.238 + (concat 1.239 + (filter (comp not zero? :duration) 1.240 + (interleave silences notes)) 1.241 + [(silence 3)])] 1.242 + notes-with-silence)) 1.243 + 1.244 +(defn midi-track->mini-midi-voice [#^File midi-file track-num] 1.245 + (let [abstract-mini-midi 1.246 + (midi-track->abstract-mini-midi midi-file track-num)] 1.247 + (map 1.248 + (fn [note-event] 1.249 + (note-codes (:frequency note-event) 1.250 + (:volume note-event) 1.251 + (int (* (:duration note-event) 0x100)))) 1.252 + abstract-mini-midi))) 1.253 + 1.254 +(def midi-code->gb-noise-code 1.255 + {nil 0xFF 1.256 + 35 87 1.257 + 38 20 1.258 + 39 0 1.259 + }) 1.260 + 1.261 +(defn noise-codes [code volume duration] 1.262 + (assert (<= 0 volume 0xF)) 1.263 + (if (<= duration 0xFF) 1.264 + [(midi-code->gb-noise-code code code) 1.265 + (bit-shift-left volume 4) 1.266 + duration] 1.267 + (vec 1.268 + (flatten 1.269 + [(noise-codes code volume 0xFF) 1.270 + (noise-codes code volume (- duration 0xFF))])))) 1.271 + 1.272 +(defn midi-track->mini-midi-noise [#^File midi-file track-num] 1.273 + (let [abstract-mini-midi 1.274 + (midi-track->abstract-mini-midi midi-file track-num)] 1.275 (map 1.276 - (fn [note-event] 1.277 - (note-codes (:frequency note-event) 1.278 - (:volume note-event) 1.279 - (int (* (:duration note-event) 0x100)))) 1.280 - notes-with-silence))) 1.281 + (fn [noise-event] 1.282 + (noise-codes (:midi-code noise-event) 1.283 + (:volume noise-event) 1.284 + (int (* (:duration noise-event) 0x100)))) 1.285 + abstract-mini-midi))) 1.286 + 1.287 1.288 (defn midi->mini-midi [#^File midi-file] 1.289 - {:track-1 (flatten (midi-track->mini-midi midi-file 1)) 1.290 - :track-2 (flatten (midi-track->mini-midi midi-file 2))}) 1.291 + (let [targets (target-tracks midi-file) 1.292 + duty-info (keys (track-info midi-file))] 1.293 + 1.294 + {:voice-1 (midi-track->mini-midi-voice midi-file (nth targets 0)) 1.295 + :voice-2 (midi-track->mini-midi-voice midi-file (nth targets 1)) 1.296 + :noise (midi-track->mini-midi-noise midi-file (nth targets 2)) 1.297 + :duty (zipmap (map :out duty-info) 1.298 + (map #(get % :duty 0) duty-info))})) 1.299 1.300 (defn play-midi [#^File midi-file] 1.301 - (let [track-1-target 0xA000 1.302 - track-2-target 0xB000 1.303 + (let [voice-1-target 0xA000 1.304 + voice-2-target 0xB000 1.305 + noise-target 0xA900 1.306 program-target 0xC000 1.307 mini-midi (midi->mini-midi midi-file) 1.308 - long-silence (flatten (note-codes 20 0 9001))] 1.309 + long-silence (flatten (note-codes 20 0 20001)) 1.310 + long-noise-silence 1.311 + (interleave (range 500) (repeat 0x00) (repeat 255)) 1.312 + 1.313 + voice-1 (flatten (:voice-1 mini-midi)) 1.314 + wave-duty-1 ((:duty mini-midi) 0 0) 1.315 + 1.316 + voice-2 (flatten (:voice-2 mini-midi)) 1.317 + wave-duty-2 ((:duty mini-midi) 1 0) 1.318 + 1.319 + noise (flatten (:noise mini-midi)) 1.320 + ] 1.321 1.322 (-> (second (music-base)) 1.323 - (set-memory-range track-1-target long-silence) 1.324 - (set-memory-range track-2-target long-silence) 1.325 - (set-memory-range track-1-target (:track-1 mini-midi)) 1.326 - (set-memory-range track-2-target (:track-2 mini-midi)) 1.327 - (set-memory-range program-target (music-kernel)) 1.328 + (set-memory-range voice-1-target long-silence) 1.329 + (set-memory-range voice-2-target long-silence) 1.330 + (set-memory-range noise-target long-noise-silence) 1.331 + (set-memory-range voice-1-target voice-1) 1.332 + (set-memory-range voice-2-target voice-2) 1.333 + (set-memory-range noise-target noise) 1.334 + (set-memory-range 1.335 + program-target 1.336 + (music-kernel wave-duty-1 wave-duty-2)) 1.337 (PC! program-target)))) 1.338 1.339 +(defn test-noise [] 1.340 + (let [noise-pattern 1.341 + (concat (interleave (range 0x100) (repeat 0xF0) (repeat 255)) 1.342 + (interleave (range 10) (repeat 0x00) (repeat 255)))] 1.343 + 1.344 + (-> (second (music-base)) 1.345 + (set-memory-range 0xA900 (flatten noise-pattern)) 1.346 + (set-memory-range 0xC000 (music-kernel 0 0)) 1.347 + (PC! 0xC000)))) 1.348 1.349 - 1.350 +(defn test-play-noise [noise-code] 1.351 + (Thread/sleep 300) 1.352 + (println "playing noise:" noise-code) 1.353 + (run-moves 1.354 + (let [noise-pattern 1.355 + (interleave (repeat 10 noise-code) (repeat 0xF0) (repeat 255))] 1.356 + (-> (second (music-base)) 1.357 + (set-memory-range 0xA900 (flatten noise-pattern)) 1.358 + (set-memory-range 0xC000 (music-kernel 0 0)) 1.359 + (PC! 0xC000))) 1.360 + (repeat 20 []))) 1.361 + 1.362 +(defn test-all-noises [] 1.363 + (dorun (map test-play-noise (range 0x100)))) 1.364 1.365 (def C4 (partial note-codes 261.63)) 1.366 (def D4 (partial note-codes 293.66)) 1.367 @@ -498,19 +663,6 @@ 1.368 (set-memory-range music-target music-bytes) 1.369 (PC! program-target)))) 1.370 1.371 - 1.372 - 1.373 -;; (defn test-note [music-bytes] 1.374 -;; (-> (set-memory-range (second (music-base)) 1.375 -;; 0xC000 (concat (clear-music-registers) 1.376 -;; (play-note) 1.377 -;; (infinite-loop))) 1.378 -;; (set-memory-range 0xD000 music-bytes) 1.379 -;; (PC! 0xC000) 1.380 -;; (HL! 0xD000) 1.381 -;; )) 1.382 - 1.383 - 1.384 (defn run-program 1.385 ([program] 1.386 (let [target 0xC000] 1.387 @@ -535,4 +687,19 @@ 1.388 [0xF0 1.389 0x05])])) 1.390 1.391 +(defn play-pony [] 1.392 + (println "playing" (.getName pony-csv)) 1.393 + (run-moves (play-midi pony-csv) (repeat 1800 []))) 1.394 1.395 +(defn play-regret [] 1.396 + (println "playing" (.getName regret-csv)) 1.397 + (run-moves (play-midi regret-csv) (repeat 3380 []))) 1.398 + 1.399 +(defn play-mother [] 1.400 + (println "playing" (.getName mother-csv)) 1.401 + (run-moves (play-midi mother-csv) (repeat 2200 []))) 1.402 + 1.403 +(defn demo [] (play-mother) (play-regret) (play-pony)) 1.404 + 1.405 + 1.406 + 1.407 \ No newline at end of file