diff clojure/com/aurellem/music/midi_util.clj @ 242:2e751984b42d

making interesting data insights.
author Dylan Holmes <ocsenave@gmail.com>
date Sun, 25 Mar 2012 02:53:29 -0500
parents
children 5b59c6f17cd5
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/clojure/com/aurellem/music/midi_util.clj	Sun Mar 25 02:53:29 2012 -0500
     1.3 @@ -0,0 +1,233 @@
     1.4 +(ns com.aurellem.music.midi-util
     1.5 +  ;;(:import javax.sound.sampled)
     1.6 +  (:import (javax.sound.midi MidiSystem
     1.7 +                             Sequence
     1.8 +                             Track
     1.9 +                             MidiEvent
    1.10 +                             MetaMessage
    1.11 +                             
    1.12 +                             ShortMessage)
    1.13 +           (com.sun.media.sound FastShortMessage)
    1.14 +           (java.io File))
    1.15 +  
    1.16 + (:use (com.aurellem.gb saves util constants gb-driver vbm items assembly characters))
    1.17 + (:use (com.aurellem.run title))
    1.18 + (:use (com.aurellem.exp pokemon item-bridge))
    1.19 + (:import [com.aurellem.gb.gb_driver SaveState]))
    1.20 +
    1.21 +
    1.22 +;;; PURE MIDI MANIPULATION
    1.23 +
    1.24 +(defn midi-load
    1.25 +  "Takes a path to a MIDI file and returns a Sequence object."
    1.26 +  [path]
    1.27 +  ((fn [file]
    1.28 +    (if (.exists file)
    1.29 +      (MidiSystem/getSequence file)
    1.30 +      nil))
    1.31 +   (File. path)))
    1.32 +
    1.33 +
    1.34 +(defn midi-play
    1.35 +  "Plays the MIDI file at the given location. The MIDI file runs in
    1.36 +the current thread until it finishes or is cancelled."
    1.37 +  [path]
    1.38 +  (let [midi (midi-load path)]
    1.39 +    (if (nil? midi) nil
    1.40 +        (let [song
    1.41 +              (doto
    1.42 +                  (MidiSystem/getSequencer) 
    1.43 +                (.open)
    1.44 +                (.setSequence midi)
    1.45 +                (.start))]
    1.46 +          (try
    1.47 +            (loop []
    1.48 +              (if (. song (isRunning))
    1.49 +                (do
    1.50 +                  (Thread/sleep 10)
    1.51 +                  (recur))
    1.52 +                (throw (Exception. "Song stopped!"))))
    1.53 +            (finally (.close song)))))))
    1.54 +
    1.55 +
    1.56 +(defn midi-test-1 []
    1.57 +  (-> (.
    1.58 +       (midi-load
    1.59 +        "/home/ocsenave/bk_robert/sounds/sounds/www.vgmusic.com/console/nintendo/gameboy/PkmRB-Item.mid")
    1.60 +       (getTracks))
    1.61 +      
    1.62 +      (vec)
    1.63 +      (second)
    1.64 +
    1.65 +      ((fn[trk]
    1.66 +         (map #(. trk (get %))
    1.67 +              (range 0 (. trk size)))))
    1.68 +
    1.69 +      ((fn [evts]
    1.70 +         (map (juxt #(identity (.getMessage %)) #(vec (.getMessage (.getMessage %))) #(.getTick %) ) evts)
    1.71 +         )
    1.72 +      
    1.73 +      )))
    1.74 +
    1.75 +
    1.76 +
    1.77 +
    1.78 +
    1.79 +
    1.80 +(defn midi-event
    1.81 +  "Creates an event at the given tick, of the given msg-type,
    1.82 +  containing data in coll. msg-type can be :meta, :short. If :meta, x
    1.83 +  is the type of the msg. If :short, x is the status of the message."
    1.84 +  [tick msg-type x coll]
    1.85 +
    1.86 +    (cond (= msg-type :meta)
    1.87 +          (MetaMessage.)
    1.88 +          (= msg-type :short)
    1.89 +          (ShortMessage.))
    1.90 +    
    1.91 +  )
    1.92 +
    1.93 +
    1.94 +(defn midi-test-2 []
    1.95 +  (let [sequence (Sequence. (float 30) 15)] ;; 30 fps, 15 frames per beat
    1.96 +    (doto (. sequence (createTrack))
    1.97 +      (.add (MidiEvent. (MetaMessage.) 0)))))
    1.98 +
    1.99 +
   1.100 +
   1.101 +            [com.sun.media.sound.FastShortMessage [-80 0 0] 0] [com.sun.media.sound.FastShortMessage [-80 7 100] 0] [com.sun.media.sound.FastShortMessage [-80 10 64] 0] [com.sun.media.sound.FastShortMessage [-80 32 0] 0] [com.sun.media.sound.FastShortMessage [-64 80] 0] [com.sun.media.sound.FastShortMessage [-80 101 0] 0] [com.sun.media.sound.FastShortMessage [-80 100 0] 1] [com.sun.media.sound.FastShortMessage [-80 6 2] 2] [com.sun.media.sound.FastShortMessage [-80 38 0] 3] [com.sun.media.sound.FastShortMessage [-32 0 56] 3] [com.sun.media.sound.FastShortMessage [-112 68 100] 3] [com.sun.media.sound.FastShortMessage [-112 68 0] 20] [com.sun.media.sound.FastShortMessage [-112 68 100] 40] [com.sun.media.sound.FastShortMessage [-112 68 0] 60] [com.sun.media.sound.FastShortMessage [-112 68 100] 80] [com.sun.media.sound.FastShortMessage [-112 68 0] 100] [com.sun.media.sound.FastShortMessage [-80 7 100] 120] [com.sun.media.sound.FastShortMessage [-112 76 100] 120] [com.sun.media.sound.FastShortMessage [-80 7 90] 181] [com.sun.media.sound.FastShortMessage [-80 7 80] 209] [com.sun.media.sound.FastShortMessage [-80 7 65] 240] [com.sun.media.sound.FastShortMessage [-80 7 50] 270] [com.sun.media.sound.FastShortMessage [-112 76 0] 300] [com.sun.media.sound.FastShortMessage [-80 7 0] 360] [javax.sound.midi.Track$ImmutableEndOfTrack [-1 47 0] 360])
   1.102 +com.aurellem.music.midi-util> (midi-test-1)
   1.103 +([javax.sound.midi.MetaMessage [-1 3 33 79 114 105 103 105 110 97 108 32 99 111 109 112 111 115 101 114 58 32 74 117 110 105 99 104 105 32 77 97 115 117 100 97] 0] [com.sun.media.sound.FastShortMessage [-80 0 0] 0] [com.sun.media.sound.FastShortMessage [-80 7 100] 0] [com.sun.media.sound.FastShortMessage [-80 10 64] 0] [com.sun.media.sound.FastShortMessage [-80 32 0] 0] [com.sun.media.sound.FastShortMessage [-64 80] 0] [com.sun.media.sound.FastShortMessage [-80 101 0] 0] [com.sun.media.sound.FastShortMessage [-80 100 0] 1] [com.sun.media.sound.FastShortMessage [-80 6 2] 2] [com.sun.media.sound.FastShortMessage [-80 38 0] 3] [com.sun.media.sound.FastShortMessage [-32 0 56] 3] [com.sun.media.sound.FastShortMessage [-112 68 100] 3] [com.sun.media.sound.FastShortMessage [-112 68 0] 20] [com.sun.media.sound.FastShortMessage [-112 68 100] 40] [com.sun.media.sound.FastShortMessage [-112 68 0] 60] [com.sun.media.sound.FastShortMessage [-112 68 100] 80] [com.sun.media.sound.FastShortMessage [-112 68 0] 100] [com.sun.media.sound.FastShortMessage [-80 7 100] 120] [com.sun.media.sound.FastShortMessage [-112 76 100] 120] [com.sun.media.sound.FastShortMessage [-80 7 90] 181] [com.sun.media.sound.FastShortMessage [-80 7 80] 209] [com.sun.media.sound.FastShortMessage [-80 7 65] 240] [com.sun.media.sound.FastShortMessage [-80 7 50] 270] [com.sun.media.sound.FastShortMessage [-112 76 0] 300] [com.sun.media.sound.FastShortMessage [-80 7 0] 360] [javax.sound.midi.Track$ImmutableEndOfTrack [-1 47 0] 360])
   1.104 +  
   1.105 +
   1.106 +;;; ROM MUSIC MANIPULATION
   1.107 +
   1.108 +(def songs;; music-headers
   1.109 +  {
   1.110 +   :pallet 0x822E
   1.111 +   :pkmn-center 0x8237
   1.112 +   :gym 0x8240
   1.113 +   :city-1 0x8249 ;;virian, pewter, saffron
   1.114 +   :city-2 0x8255 ;; cerulean, fuchsia
   1.115 +   :celedon 0x825E
   1.116 +   :cinnibar 0x8267
   1.117 +   :vermilion 0x8270
   1.118 +   :lavender 0x827C
   1.119 +   :ss-anne 0x8288
   1.120 +   :meet-prof 0x8291
   1.121 +   :meet-blue 0x829A
   1.122 +   :follow 0x82A3
   1.123 +   :safari 0x82AF
   1.124 +   :sfx-heal 0x82BA
   1.125 +   :route-1 0x82C1 ;; route 1,2
   1.126 +   :route-2 0x82CD ;; route 24, 25
   1.127 +   :route-3 0x82D9 ;; route 3-10,16-22
   1.128 +   :route-4 0x82E5 ;; route 11-15
   1.129 +   :route-5 0x82F1 ;; indigo plateau
   1.130 +
   1.131 +   :title 0x7C249
   1.132 +   :credits 0x7C255
   1.133 +   :hall-of-fame 0x7C25E
   1.134 +   :lab-prof 0x7C267
   1.135 +   :jigglypuff 0x7C270
   1.136 +   :bike 0x7C276
   1.137 +   :surfing 0x7C282
   1.138 +   :casino 0x7C28B
   1.139 +   :intro-battle 0x7C294
   1.140 +   :power-plant 0x7C2A0 ;; power plant, unknown dungeon
   1.141 +   :viridian-forest 0x7C2AC ;;viridian forest, seafoam islands
   1.142 +   :victory-rd 0x7C2B8 ;;mt moon, rock tunnel, victory rd 
   1.143 +   :mansion 0x7C2C4
   1.144 +   :pkmn-tower 0x7C2D0
   1.145 +   :silph 0x7C2D9
   1.146 +   :trainer-bad 0x7C2E2
   1.147 +   :trainer-girl 0x7C2EB 
   1.148 +   :trainer-angry 0x7C2F4
   1.149 +   })
   1.150 +   
   1.151 +
   1.152 +   })
   1.153 +
   1.154 +
   1.155 +(defn low-high-format
   1.156 +  "Returns the number represented by the bytes."
   1.157 +  [low high]
   1.158 +  (+ low (* 256  high)))
   1.159 +
   1.160 +(defn rom-tracks
   1.161 +  "Given a valid address to a music header, returns a list of the
   1.162 +data tracks"
   1.163 +  [address]
   1.164 +  (let [rom (rom (root))
   1.165 +        tracklist
   1.166 +        ((fn extract-tracklist [mem n]
   1.167 +           (if (= (nth mem 2) n)
   1.168 +             (cons (low-high-format (first mem)
   1.169 +                       (second mem))
   1.170 +                   (extract-tracklist (drop 3 mem) (inc n)))              
   1.171 +             '()))
   1.172 +     
   1.173 +         (take 12 (drop (inc address) rom))
   1.174 +         1
   1.175 +         )]
   1.176 +
   1.177 +    (map
   1.178 +     (fn [trk] (take-while #(not= 0xFF %) (drop trk rom)))
   1.179 +     tracklist)
   1.180 +
   1.181 +    ))
   1.182 +  
   1.183 +
   1.184 +
   1.185 +(defn parse-track
   1.186 +  "Consumes the list of opcodes, returning a runnable MIDI Sequence object."
   1.187 +  [track]
   1.188 +  (fn [midi track]
   1.189 +    (cond (empty? track) midi)))
   1.190 +
   1.191 +
   1.192 +
   1.193 +
   1.194 +
   1.195 +
   1.196 +
   1.197 +
   1.198 +
   1.199 +  
   1.200 +;; 8237-823F Pokecenter 
   1.201 +;; 8240-8248 Gym 
   1.202 +;; 8249-8254 Viridian / Pewter / Saffron 
   1.203 +;; 8255-825D Cerulean / Fuchsia 
   1.204 +;; 825E-8266 Celedon 
   1.205 +;; 8267-826F Cinnibar 
   1.206 +;; 8270-827B Vermilion 
   1.207 +;; 827C-8287 Lavender 
   1.208 +;; 8288-8290 S.S. Anne 
   1.209 +;; 8291-8299 Meet Prof. Oak 
   1.210 +;; 829A-82A2 Meet Rival 
   1.211 +;; 82A3-82AE Guy Walks you to Museum 
   1.212 +;; 82AF-82B7 Safari Zone 
   1.213 +;; 82B8-82C0 Pokemon get healed 
   1.214 +;; 82C1-82CC Routes 1 / 2 
   1.215 +;; 82CD-82D8 Routes 24 / 25 
   1.216 +;; 82D9-82E4 Routes 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 16 / 17 / 18 / 19 / 20 / 21 / 22 
   1.217 +;; 82E5-82F0 Routes 11 / 12 / 13 / 14 / 15 
   1.218 +;; 82F1-82FD Route 23 / Indigo Plateau 
   1.219 +;; 7C249-7C254 Title Screen 
   1.220 +;; 7C255-7C25D Credits 
   1.221 +;; 7C25E-7C266 Hall of FAme Registration 
   1.222 +;; 7C267-7C26F PRof Oak's LAb 
   1.223 +;; 7C270-7C275 Jigglypuff's Song 
   1.224 +;; 7C276-7C281 Bike Riding 
   1.225 +;; 7C282-7C28A Surfing 
   1.226 +;; 7C28B-7C293 Casino 
   1.227 +;; 7C294-7C29F Introduction Battle 
   1.228 +;; 7C2A0-7C2AB Power Plant / Unknown Dungeon 
   1.229 +;; 7C2AC-7C2B7 Viridian Forest / Seafoam Islands 
   1.230 +;; 7C2B8-7C2C3 Mt. Moon / Rock Tunnel / Victory Road 
   1.231 +;; 7C2C4-7C2CF Cinnibar Mansion 
   1.232 +;; 7C2D0-7C2D8 Pokemon Tower 
   1.233 +;; 7C2D9-7C2E1 Silph Co 
   1.234 +;; 7C2E2-7C2EA Meet Bad Trainer 
   1.235 +;; 7C2EB-7C2F3 Meet Girl Trainer 
   1.236 +;; 7C2F4-7C2FC Meet Angry Trainer