Mercurial > vba-clojure
view clojure/com/aurellem/run/music.clj @ 425:df4e03672b05
implemented note-code message.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 23 Apr 2012 05:45:25 -0500 |
parents | 7bd806c4dbb6 |
children | c03f28aa98d9 |
line wrap: on
line source
1 (ns com.aurellem.run.music2 (:use (com.aurellem.gb saves gb-driver util constants3 items vbm characters money4 rlm-assembly))5 (:use (com.aurellem.run util title save-corruption6 bootstrap-0 bootstrap-1))7 (:import [com.aurellem.gb.gb_driver SaveState]))10 (def music-base new-kernel)15 (defn store [n address]16 (flatten17 [0xF518 0xE520 0x3E21 n23 0x2124 (reverse (disect-bytes-2 address))26 0x7728 0xE129 0xF1]))31 (defn infinite-loop []32 [0x18 0xFE])36 (def divider-register 0xFF04)39 (defrecord Bit-Note [frequency volume duration duty])41 (defn clear-music-registers []42 (flatten43 [(store (Integer/parseInt "00000000" 2) 0xFF10)44 (store (Integer/parseInt "00000000" 2) 0xFF11)45 (store (Integer/parseInt "00000000" 2) 0xFF12)46 (store (Integer/parseInt "00000000" 2) 0xFF13)47 (store (Integer/parseInt "00000000" 2) 0xFF14)49 (store (Integer/parseInt "00000000" 2) 0xFF16) ;; pattern duty 00000050 (store (Integer/parseInt "00000000" 2) 0xFF17) ;; volume 000051 (store (Integer/parseInt "00000000" 2) 0xFF18) ;; frequency-low52 (store (Integer/parseInt "00000000" 2) 0xFF19) ;; 00000 frequency-high54 (store (Integer/parseInt "00000000" 2) 0xFF1A)55 (store (Integer/parseInt "00000000" 2) 0xFF1B)56 (store (Integer/parseInt "00000000" 2) 0xFF1C)57 (store (Integer/parseInt "00000000" 2) 0xFF1D)58 (store (Integer/parseInt "00000000" 2) 0xFF1E)60 (store (Integer/parseInt "00000000" 2) 0xFF20)61 (store (Integer/parseInt "00000000" 2) 0xFF21)62 (store (Integer/parseInt "00000000" 2) 0xFF22)63 (store (Integer/parseInt "00000000" 2) 0xFF23)]))66 ;; mini-midi syntax68 ;; codes69 ;; note-code == 0x0070 ;; change-duty-code = 0x0171 ;; silence-code = 0x0273 ;; silence format74 ;; 2 bytes75 ;; [silence-code (0x02)]76 ;; [duration-8-bits]78 ;; note data format79 ;; 4 bytes80 ;; [note-code (0x00)]81 ;; [volume-4-bits 0 frequency-high-3-bits]82 ;; [frequengy-low-8-bits]83 ;; [duration-8-bits]85 ;; change-duty-format86 ;; 2 bytes87 ;; [change-duty-code (0x01)]88 ;; [new-duty]90 (def note-code 0x00)91 (def change-duty-code 0x01)92 (def silence-code 0x02)95 (defn do-message96 "Read the message which starts at the current value of HL and do97 what it says. Duration is left in A, and HL is advanced98 appropraitely."99 []100 (let [switch101 [0x2A ;; load message code into A, increment HL103 ;; switch on message104 0xFE105 note-code107 0x20108 :note-length]110 play-note111 [0x2A ;; load volume/frequency-high info112 0xF5 ;; push A113 0xE6114 (Integer/parseInt "11110000" 2) ;; volume mask115 0xE0116 0x17 ;; set volume117 0xF1 ;; pop A118 0xE6119 (Integer/parseInt "00000111" 2) ;; frequency-high mask120 0xE0121 0x19 ;; set frequency-high123 0x2A ;; load frequency low-bits124 0xE0125 0x18 ;; set frequency-low-bits127 0x2A]] ;; load duration128 (replace129 {:note-length (count play-note)}130 (concat switch play-note))))132 (defn play-note133 "Play the note referenced by HL in the appropiate channel.134 Leaves desired-duration in A."135 []136 [0x2A ;; load volume/frequency-high info137 0xF5 ;; push A138 0xE6139 (Integer/parseInt "11110000" 2) ;; volume mask140 0xE0141 0x17 ;; set volume142 0xF1 ;; pop A143 0xE6144 (Integer/parseInt "00000111" 2) ;; frequency-high mask145 0xE0146 0x19 ;; set frequency-high148 0x2A ;; load frequency low-bits149 0xE0150 0x18 ;; set frequency-low-bits152 0x2A ;; load duration153 ])155 (defn music-step []156 (flatten157 [158 0xF5 ;; push A159 0xF0160 0x05 ;; load current ticks161 0xB8 ;; B holds previous sub-ticks, subtract it from A162 ;; if A-B caused a carry, then (B > A) is true, and163 ;; A = current-sub-tics, B = previous-sub-ticks, so164 ;; current-sub-ticks < previous-sub-ticks, which means that the165 ;; timer counter HAS overflowed.166 0x30 ;; increment C only if last result caused carry167 0x01168 0x0C170 0x47 ;; update sub-ticks (A->B)172 0xF1 ;; pop AF, now A contains desired-ticks174 0xB9 ;; compare with current ticks176 ;; if desired-ticks = current ticks177 ;; go to next note ; set current set ticks to 0.179 0x20180 (+ (count (do-message)) 2)182 (do-message)184 0x0E185 0x00])) ;; 0->C (current-ticks)187 (defn music-kernel []188 (flatten189 [(clear-music-registers)191 0x21192 0x00193 0xD0 ;; set HL to 0xD000 == music-start194 0x0E195 0x00 ;; 0->C196 0x06197 0x00 ;; 0->B199 0x3E200 0x01201 0xE0202 0x06 ;; set TMA to 0204 0x3E205 (Integer/parseInt "00000110" 2)206 0xE0207 0x07 ;; set TAC to 65536 Hz and activate timer210 0xAF ;; initialiaze A to zero213 (music-step)214 0x18215 (->signed-8-bit (+ (- (count (music-step)))216 -2))]))218 (defn frequency-code->frequency219 [code]220 (assert (<= 0 code 2047))221 (/ 131072 (- 2048 code)))223 (defn clamp [x low high]224 (cond (> x high) high225 (< x low) low226 true x))228 (defn frequency->frequency-code229 [frequency]230 (clamp231 (Math/round232 (float233 (/ (- (* 2048 frequency) 131072) frequency)))234 0x00 2048))236 (defn note-codes [frequency volume duration]237 (assert (<= 0 volume 0xF))238 (assert (<= 0 duration 0xFF))239 (let [frequency-code240 (frequency->frequency-code frequency)241 volume&high-frequency242 (+ (bit-shift-left volume 4)243 (bit-shift-right frequency-code 8))244 low-frequency245 (bit-and 0xFF frequency-code)]246 [note-code247 volume&high-frequency248 low-frequency249 duration]))251 (def C4 (partial note-codes 261.63))252 (def D4 (partial note-codes 293.66))253 (def E4 (partial note-codes 329.63))254 (def F4 (partial note-codes 349.23))255 (def G4 (partial note-codes 392))256 (def A4 (partial note-codes 440))257 (def B4 (partial note-codes 493.88))258 (def C5 (partial note-codes 523.3))260 (def scale261 (flatten262 [(C4 0xF 0x40)263 (D4 0xF 0x40)264 (E4 0xF 0x40)265 (F4 0xF 0x40)266 (G4 0xF 0x40)267 (A4 0xF 0x40)268 (B4 0xF 0x40)269 (C5 0xF 0x40)]))271 (defn play-music [music-bytes]272 (let [program-target 0xC000273 music-target 0xD000]274 (-> (set-memory-range (second (music-base))275 program-target (music-kernel))276 (set-memory-range music-target music-bytes)277 (PC! program-target))))280 (defn test-note [music-bytes]281 (-> (set-memory-range (second (music-base))282 0xC000 (concat (clear-music-registers)283 (play-note)284 (infinite-loop)))285 (set-memory-range 0xD000 music-bytes)286 (PC! 0xC000)287 (HL! 0xD000)288 ))291 (defn run-program292 ([program]293 (let [target 0xC000]294 (-> (set-memory-range (second (music-base))295 target program)296 (PC! target)))))298 (defn trippy []299 (run-moves (play-music many-notes ) (repeat 8000 [])))301 (defn test-timer []302 (flatten303 [0x3E304 0x01305 0xE0306 0x06 ;; set TMA to 0308 0x3E309 (Integer/parseInt "00000100" 2)310 0xE0311 0x07 ;; set TAC to 16384 Hz and activate timer313 (repeat314 500315 [0xF0316 0x05])]))