Mercurial > vba-clojure
view clojure/com/aurellem/music/midi_util.clj @ 276:18336ab5d6ea
merge.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 27 Mar 2012 12:37:48 -0500 |
parents | 5b59c6f17cd5 |
children | 2873f50b7291 |
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 constants gb-driver vbm items assembly characters))14 (:use (com.aurellem.run title))15 (:use (com.aurellem.exp pokemon item-bridge))16 (:import [com.aurellem.gb.gb_driver SaveState]))19 ;;; PURE MIDI MANIPULATION21 (defn midi-load22 "Takes a path to a MIDI file and returns a Sequence object."23 [path]24 ((fn [file]25 (if (.exists file)26 (MidiSystem/getSequence file)27 nil))28 (File. path)))32 (def midi-play-file33 "Plays the MIDI file at the given location. The MIDI file runs in34 the current thread until it finishes or is cancelled."35 (comp midi-play-seq midi-load))38 (defn midi-play-seq39 "Plays the MIDI Sequence. The MIDI runs in40 the current thread until it finishes or is cancelled."41 [midi]42 (if (nil? midi) nil43 (let [song44 (doto45 (MidiSystem/getSequencer)46 (.open)47 (.setSequence midi)48 (.start))]49 (try50 (loop []51 (if (. song (isRunning))52 (do53 (Thread/sleep 10)54 (recur))55 ))56 (finally (.close song))))))59 (defn midi-test-1 []60 (-> (.61 (midi-load62 "/home/ocsenave/bk_robert/sounds/sounds/www.vgmusic.com/console/nintendo/gameboy/PkmRB-Item.mid")63 (getTracks))65 (vec)66 (nth 1)68 ((fn[trk]69 (map #(. trk (get %))70 (range 0 (. trk size)))))72 ((fn [evts]73 (map (juxt #(.getTick %) #(vec (.getMessage (.getMessage74 %))) #(.getMessage %) ) evts)75 )77 )))83 (defn midi-short84 "Creates a MIDI event containing a ShortMessage."85 [tick [status & ns]]86 (MidiEvent.87 (apply88 (fn89 ([x] (doto (ShortMessage.) (.setMessage x)))90 ([x y] (doto (ShortMessage.) (.setMessage x y 0)))91 ([x y z] (doto (ShortMessage.) (.setMessage x y z)))92 ([x y z w] (doto (ShortMessage.) (.setMessage x y z w))))93 status94 ns)95 tick))97 (defn midi-meta98 "Creates a MIDI event containing a MetaMessage"99 [tick type ns]100 (MidiEvent.101 (doto (MetaMessage.)102 (.setMessage type103 (byte-array (map byte ns))104 (count ns)))105 tick))109 (defn sign110 "Interpret the bits of n as a signed two's-complement byte"111 [n]112 (if (>= n 128) (- n 256)113 n))115 (defn unsign116 "Interpret the bits as an unsigned byte."117 [n]118 (if (neg? n) (+ n 256) n))123 (defn midi-test-2 []124 (let [sequence (Sequence. (float 30) 12)] ;; 30 fps, 10 frames per beat125 (doto (. sequence (createTrack))126 (.add (midi-meta 0 3 [-1 3 33 79 114 105 103 105 110 97 108 32127 99 111 109 112 111 115 101 114 58 32 74 117 110 105 99 104 105 32 77128 97 115 117 100 97]))130 (.add (midi-short 0 [-80 0 0])) ;; control change = -80131 (.add (midi-short 0 [-80 7 100])) ;; control change, volume, 100132 (.add (midi-short 0 [-80 10 64])) ;; control change, pan, 64 (middle?)133 (.add (midi-short 0 [-80 32 0])) ;; ctrl chg, LSB ctrl 0 = bank 0134 (.add (midi-short 0 [-64 01])) ;; program/instrument change = -64135 (.add (midi-short 0 [-80 101 0]))136 (.add (midi-short 1 [-80 100 0]))137 (.add (midi-short 2 [-80 6 2]))138 (.add (midi-short 3 [-80 38 0]))139 (.add (midi-short 3 [-32 0 56])) ;; pitch bend?! = -32140 (.add (midi-short 3 [-112 68 100])) ;; note on = -112141 (.add (midi-short 20 [-112 68 0]))142 (.add (midi-short 40 [-112 68 100]))143 (.add (midi-short 60 [-112 68 0]))144 (.add (midi-short 80 [-112 68 100]))145 (.add (midi-short 100 [-112 68 0]))146 (.add (midi-short 120 [-80 7 100])) ;; control change147 (.add (midi-short 120 [-112 76 100])) ;; note-on148 (.add (midi-short 181 [-80 7 90])) ;; control change149 (.add (midi-short 209 [-80 7 80])) ;; control change150 (.add (midi-short 240 [-80 7 65])) ;; control change151 (.add (midi-short 270 [-80 7 50])) ;; control change152 (.add (midi-short 300 [-112 76 0])) ;; note on = -112153 (.add (midi-short 360 [-80 7 0])))154 ;;(.add (midi-short 360 [-1 47 0])) ;; system reset = -1156 sequence157 )))162 ;;; ROM MUSIC MANIPULATION164 (def songs;; music-headers165 {166 :pallet 0x822E167 :pkmn-center 0x8237168 :gym 0x8240169 :city-1 0x8249 ;;virian, pewter, saffron170 :city-2 0x8255 ;; cerulean, fuchsia171 :celedon 0x825E172 :cinnibar 0x8267173 :vermilion 0x8270174 :lavender 0x827C175 :ss-anne 0x8288176 :meet-prof 0x8291177 :meet-blue 0x829A178 :follow 0x82A3179 :safari 0x82AF180 :sfx-heal 0x82BA181 :route-1 0x82C1 ;; route 1,2182 :route-2 0x82CD ;; route 24, 25183 :route-3 0x82D9 ;; route 3-10,16-22184 :route-4 0x82E5 ;; route 11-15185 :route-5 0x82F1 ;; indigo plateau187 :title 0x7C249188 :credits 0x7C255189 :hall-of-fame 0x7C25E190 :lab-prof 0x7C267191 :jigglypuff 0x7C270192 :bike 0x7C276193 :surfing 0x7C282194 :casino 0x7C28B195 :intro-battle 0x7C294196 :power-plant 0x7C2A0 ;; power plant, unknown dungeon197 :viridian-forest 0x7C2AC ;;viridian forest, seafoam islands198 :victory-rd 0x7C2B8 ;;mt moon, rock tunnel, victory rd199 :mansion 0x7C2C4200 :pkmn-tower 0x7C2D0201 :silph 0x7C2D9202 :trainer-bad 0x7C2E2203 :trainer-girl 0x7C2EB204 :trainer-angry 0x7C2F4205 })208 })211 (defn low-high-format212 "Returns the number represented by the bytes."213 [low high]214 (+ low (* 256 high)))215 (defn high-low-format216 "Returns the number represented by the bytes."217 [high low]218 (+ low (* 256 high)))221 (defn rom-tracks222 "Given a valid address to a music header, returns a list of the223 data tracks"224 [address]225 (let [rom (rom (root))226 tracklist227 ((fn extract-tracklist [mem n]228 (if (= (nth mem 2) n)229 (cons (low-high-format (first mem)230 (second mem))231 (extract-tracklist (drop 3 mem) (inc n)))232 '()))234 (take 12 (drop (inc address) rom))235 1236 )]238 tracklist239 (map240 (fn [trk] (take-while #(not= 0xFF %) (drop trk rom)))241 tracklist)243 ))248 (defn note?249 "Does the given byte correspond to a note?"250 [n]252 (defn parse-ops253 "Consumes the list of opcodes, returning a runnable MIDI Sequence object."254 [ops]255 (256 (fn [midi ops]257 (let [x (first ops)]258 (cond (empty? ops) midi259 (= x 0xDA)260 ;; set tempo (high-low (nth ops 1)(nth ops 2))261 (recur (identity midi) (drop 3 ops))263 (note? x)266 )268 (doto (Sequence. (float 30) 15) ;; 30 fps, 15 frames per beat269 (.createTrack))270 ops271 ))279 ;; 8237-823F Pokecenter280 ;; 8240-8248 Gym281 ;; 8249-8254 Viridian / Pewter / Saffron282 ;; 8255-825D Cerulean / Fuchsia283 ;; 825E-8266 Celedon284 ;; 8267-826F Cinnibar285 ;; 8270-827B Vermilion286 ;; 827C-8287 Lavender287 ;; 8288-8290 S.S. Anne288 ;; 8291-8299 Meet Prof. Oak289 ;; 829A-82A2 Meet Rival290 ;; 82A3-82AE Guy Walks you to Museum291 ;; 82AF-82B7 Safari Zone292 ;; 82B8-82C0 Pokemon get healed293 ;; 82C1-82CC Routes 1 / 2294 ;; 82CD-82D8 Routes 24 / 25295 ;; 82D9-82E4 Routes 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 16 / 17 / 18 / 19 / 20 / 21 / 22296 ;; 82E5-82F0 Routes 11 / 12 / 13 / 14 / 15297 ;; 82F1-82FD Route 23 / Indigo Plateau298 ;; 7C249-7C254 Title Screen299 ;; 7C255-7C25D Credits300 ;; 7C25E-7C266 Hall of FAme Registration301 ;; 7C267-7C26F PRof Oak's LAb302 ;; 7C270-7C275 Jigglypuff's Song303 ;; 7C276-7C281 Bike Riding304 ;; 7C282-7C28A Surfing305 ;; 7C28B-7C293 Casino306 ;; 7C294-7C29F Introduction Battle307 ;; 7C2A0-7C2AB Power Plant / Unknown Dungeon308 ;; 7C2AC-7C2B7 Viridian Forest / Seafoam Islands309 ;; 7C2B8-7C2C3 Mt. Moon / Rock Tunnel / Victory Road310 ;; 7C2C4-7C2CF Cinnibar Mansion311 ;; 7C2D0-7C2D8 Pokemon Tower312 ;; 7C2D9-7C2E1 Silph Co313 ;; 7C2E2-7C2EA Meet Bad Trainer314 ;; 7C2EB-7C2F3 Meet Girl Trainer315 ;; 7C2F4-7C2FC Meet Angry Trainer