Mercurial > vba-clojure
view clojure/com/aurellem/music/midi_util.clj @ 430:c709f4857fa9
correctly handles notes longer than 1 second.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 23 Apr 2012 09:49:24 -0500 |
parents | e6a5dfd31230 |
children | ea5ed834be11 |
line wrap: on
line source
1 (ns com.aurellem.music.midi-util2 ;;(:import javax.sound.sampled)3 (:import (javax.sound.midi MidiSystem4 Sequence5 Track6 MidiEvent7 MetaMessage9 ShortMessage)10 (com.sun.media.sound FastShortMessage)11 (java.io File))13 (:use (com.aurellem.gb saves util mem-util constants gb-driver vbm items assembly characters))14 (:use (com.aurellem.run title))15 (:use (com.aurellem.exp pokemon item-bridge))16 ;; (:use (com.aurellem.world practice))17 (:import [com.aurellem.gb.gb_driver SaveState]))20 ;;; PURE MIDI MANIPULATION22 (defn midi-load23 "Takes a path to a MIDI file and returns a Sequence object."24 [path]25 ((fn [file]26 (if (.exists file)27 (MidiSystem/getSequence file)28 nil))29 (File. path)))33 (defn midi-play-seq34 "Plays the MIDI Sequence. The MIDI runs in35 the current thread until it finishes or is cancelled."36 [midi]37 (if (nil? midi) nil38 (let [song39 (doto40 (MidiSystem/getSequencer)41 (.open)42 (.setSequence midi)43 (.start))]44 (try45 (loop []46 (if (. song (isRunning))47 (do48 (Thread/sleep 10)49 (recur))50 ))51 (finally (.close song))))))54 (def midi-play-file55 "Plays the MIDI file at the given location. The MIDI file runs in56 the current thread until it finishes or is cancelled."57 (comp midi-play-seq midi-load))62 (defn midi-test-1 []63 (-> (.64 (midi-load65 "/home/ocsenave/bk_robert/sounds/sounds/www.vgmusic.com/console/nintendo/gameboy/PkmRB-Item.mid")66 (getTracks))68 (vec)69 (nth 1)71 ((fn[trk]72 (map #(. trk (get %))73 (range 0 (. trk size)))))75 ((fn [evts]76 (map (juxt #(.getTick %) #(vec (.getMessage (.getMessage77 %))) #(.getMessage %) ) evts)78 )80 )))86 (defn midi-short87 "Creates a MIDI event containing a ShortMessage."88 [tick [status & ns]]89 (MidiEvent.90 (apply91 (fn92 ([x] (doto (ShortMessage.) (.setMessage x)))93 ([x y] (doto (ShortMessage.) (.setMessage x y 0)))94 ([x y z] (doto (ShortMessage.) (.setMessage x y z)))95 ([x y z w] (doto (ShortMessage.) (.setMessage x y z w))))96 status97 ns)98 tick))100 (defn midi-meta101 "Creates a MIDI event containing a MetaMessage"102 [tick type ns]103 (MidiEvent.104 (doto (MetaMessage.)105 (.setMessage type106 (byte-array (map byte ns))107 (count ns)))108 tick))112 (defn sign113 "Interpret the bits of n as a signed two's-complement byte"114 [n]115 (if (>= n 128) (- n 256)116 n))118 (defn unsign119 "Interpret the bits as an unsigned byte."120 [n]121 (if (neg? n) (+ n 256) n))126 (defn midi-test-2 []127 (let [sequence (Sequence. (float 30) 12)] ;; 30 fps, 10 frames per beat128 (doto (. sequence (createTrack))129 (.add (midi-meta 0 3 [-1 3 33 79 114 105 103 105 110 97 108 32130 99 111 109 112 111 115 101 114 58 32 74 117 110 105 99 104 105 32 77131 97 115 117 100 97]))133 (.add (midi-short 0 [-80 0 0])) ;; control change = -80134 (.add (midi-short 0 [-80 7 100])) ;; control change, volume, 100135 (.add (midi-short 0 [-80 10 64])) ;; control change, pan, 64 (middle?)136 (.add (midi-short 0 [-80 32 0])) ;; ctrl chg, LSB ctrl 0 = bank 0137 (.add (midi-short 0 [-64 01])) ;; program/instrument change = -64138 (.add (midi-short 0 [-80 101 0]))139 (.add (midi-short 1 [-80 100 0]))140 (.add (midi-short 2 [-80 6 2]))141 (.add (midi-short 3 [-80 38 0]))142 (.add (midi-short 3 [-32 0 56])) ;; pitch bend?! = -32143 (.add (midi-short 3 [-112 68 100])) ;; note on = -112144 (.add (midi-short 20 [-112 68 0]))145 (.add (midi-short 40 [-112 68 100]))146 (.add (midi-short 60 [-112 68 0]))147 (.add (midi-short 80 [-112 68 100]))148 (.add (midi-short 100 [-112 68 0]))149 (.add (midi-short 120 [-80 7 100])) ;; control change150 (.add (midi-short 120 [-112 76 100])) ;; note-on151 (.add (midi-short 181 [-80 7 90])) ;; control change152 (.add (midi-short 209 [-80 7 80])) ;; control change153 (.add (midi-short 240 [-80 7 65])) ;; control change154 (.add (midi-short 270 [-80 7 50])) ;; control change155 (.add (midi-short 300 [-112 76 0])) ;; note on = -112156 (.add (midi-short 360 [-80 7 0])))157 ;;(.add (midi-short 360 [-1 47 0])) ;; system reset = -1159 sequence160 ))165 (defn read-memory166 ([mem start length]167 (take length168 (drop start169 mem)))170 ([start length]171 (read-memory (rom(root)) start length)))173 ;;; ROM MUSIC MANIPULATION175 (def songs;; music-headers176 {177 :pallet 0x822E178 :pkmn-center 0x8237179 :gym 0x8240180 :city-1 0x8249 ;;virian, pewter, saffron181 :city-2 0x8255 ;; cerulean, fuchsia182 :celedon 0x825E183 :cinnibar 0x8267184 :vermilion 0x8270185 :lavender 0x827C186 :ss-anne 0x8288187 :meet-prof 0x8291188 :meet-blue 0x829A189 :follow 0x82A3190 :evolution 0x82AF191 :sfx-heal 0x82B8192 :route-1 0x82C1 ;; route 1,2193 :route-2 0x82CD ;; route 24, 25194 :route-3 0x82D9 ;; route 3-10,16-22195 :route-4 0x82E5 ;; route 11-15196 :route-5 0x82F1 ;; indigo plateau198 ;;:1 0xc977 ;; fourteen "tracks" X199 :1 0x801cb ;; slot machine music200 :2 0x801d4201 :3 0x801dd203 :4 0x202be ;; uber slow battle songs?204 :5 0x202c7 ;; can't play with pallet or bike (wrong base)205 :6 0x202d9206 :7 0x202e2207 :8 0x202eb208 :9 0x202f4210 :title 0x7C249211 :credits 0x7C255212 :hall-of-fame 0x7C25E213 :lab-prof 0x7C267214 :jigglypuff 0x7C270215 :bike 0x7C276216 :surfing 0x7C282217 :casino 0x7C28B218 :intro 0x7C294219 :power-plant 0x7C29D ;; power plant, unknown dungeon220 :viridian-forest 0x7C2A9 ;;viridian forest, seafoam islands221 :victory-rd 0x7C2B5 ;;mt moon, rock tunnel, victory rd222 :mansion 0x7C2C1223 :pkmn-tower 0x7C2CD224 :silph 0x7C2D6225 :trainer-bad 0x7C2DF226 :trainer-girl 0x7C2E8227 :trainer-angry 0x7C2F1228 })231 (defn low-high-format232 "Returns the number represented by the bytes."233 [low high]234 (+ low (* 256 high)))235 (defn high-low-format236 "Returns the number represented by the bytes."237 [high low]238 (+ low (* 256 high)))241 (defn rom-tracks242 "Given a valid address to a music header, returns a list of the243 data tracks"244 [address]245 (let [rom (rom (root))246 tracklist247 ((fn extract-tracklist [mem n]248 (if (= (nth mem 2) n)249 (cons (low-high-format (first mem)250 (second mem))251 (extract-tracklist (drop 3 mem) (inc n)))252 '()))254 (take 12 (drop (inc address) rom))255 1256 )]258 tracklist259 (map260 (fn [trk] (take-while #(not= 0xFF %) (drop trk rom)))261 tracklist)263 ))267 (defn find-music []268 (let [extract-song269 (fn [mem]270 (if (and271 (> (count mem) 6)272 (zero? (rem (first mem) 16))273 (not(zero? (first mem)))274 (= (nth mem 3) 1)275 (= (nth mem 6) 2))276 (take 12 mem)))]277 (loop [mem (rom)278 results []279 ptr 0]280 (cond (empty? mem)281 results283 (nil? (extract-song mem))284 (recur (rest mem)285 results286 (inc ptr))287 :else288 (recur (rest mem)289 (conj results290 [(hex ptr) (extract-song mem)])291 (inc ptr))))))293 (defn music-header294 "Given a valid address to a music header, returns the music header."295 [address]296 (let [297 rom (rom)298 data (drop address rom)299 ]300 (->301 (loop [n 1 k 3]302 (if (= n (nth data k))303 (recur (inc n) (+ k 3))304 k))305 (take data))))307 (defn pallet-song [song-name]308 (write-rom!309 (rewrite-memory310 (vec(rom))311 0x822e312 (music-header (songs song-name)))))314 (defn bike-song [song-name]315 (write-rom!316 (rewrite-memory317 (vec(rom))318 0x7c276319 (music-header (songs song-name)))))321 (defn bike-song* [address]322 (write-rom!323 (rewrite-memory324 (vec(rom))325 0x7c276326 (music-header address))))329 (defn pallet-song* [address]330 (write-rom!331 (rewrite-memory332 (vec(rom))333 0x822e334 (music-header address))))335 ;; (defn note?336 ;; "Does the given byte correspond to a note?"337 ;; [n])339 ;; (comment defn parse-ops340 ;; "Consumes the list of opcodes, returning a runnable MIDI Sequence object."341 ;; [ops]342 ;; (343 ;; (fn [midi ops]344 ;; (let [x (first ops)]345 ;; (cond (empty? ops) midi346 ;; (= x 0xDA)347 ;; ;; set tempo (high-low (nth ops 1)(nth ops 2))348 ;; (recur (identity midi) (drop 3 ops))350 ;; (note? x)353 ;; )355 ;; (doto (Sequence. (float 30) 15) ;; 30 fps, 15 frames per beat356 ;; (.createTrack))357 ;; ops358 ;; ))))366 ;; 8237-823F Pokecenter367 ;; 8240-8248 Gym368 ;; 8249-8254 Viridian / Pewter / Saffron369 ;; 8255-825D Cerulean / Fuchsia370 ;; 825E-8266 Celedon371 ;; 8267-826F Cinnibar372 ;; 8270-827B Vermilion373 ;; 827C-8287 Lavender374 ;; 8288-8290 S.S. Anne375 ;; 8291-8299 Meet Prof. Oak376 ;; 829A-82A2 Meet Rival377 ;; 82A3-82AE Guy Walks you to Museum378 ;; 82AF-82B7 Safari Zone379 ;; 82B8-82C0 Pokemon get healed380 ;; 82C1-82CC Routes 1 / 2381 ;; 82CD-82D8 Routes 24 / 25382 ;; 82D9-82E4 Routes 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 16 / 17 / 18 / 19 / 20 / 21 / 22383 ;; 82E5-82F0 Routes 11 / 12 / 13 / 14 / 15384 ;; 82F1-82FD Route 23 / Indigo Plateau385 ;; 7C249-7C254 Title Screen386 ;; 7C255-7C25D Credits387 ;; 7C25E-7C266 Hall of FAme Registration388 ;; 7C267-7C26F PRof Oak's LAb389 ;; 7C270-7C275 Jigglypuff's Song390 ;; 7C276-7C281 Bike Riding391 ;; 7C282-7C28A Surfing392 ;; 7C28B-7C293 Casino393 ;; 7C294-7C29F Introduction Battle394 ;; 7C2A0-7C2AB Power Plant / Unknown Dungeon395 ;; 7C2AC-7C2B7 Viridian Forest / Seafoam Islands396 ;; 7C2B8-7C2C3 Mt. Moon / Rock Tunnel / Victory Road397 ;; 7C2C4-7C2CF Cinnibar Mansion398 ;; 7C2D0-7C2D8 Pokemon Tower399 ;; 7C2D9-7C2E1 Silph Co400 ;; 7C2E2-7C2EA Meet Bad Trainer401 ;; 7C2EB-7C2F3 Meet Girl Trainer402 ;; 7C2F4-7C2FC Meet Angry Trainer