view clojure/com/aurellem/music/midi_util.clj @ 258:2a46422902be

script: got pikachu.
author Robert McIntyre <rlm@mit.edu>
date Mon, 26 Mar 2012 18:01:39 -0500
parents 5b59c6f17cd5
children 2873f50b7291
line wrap: on
line source
1 (ns com.aurellem.music.midi-util
2 ;;(:import javax.sound.sampled)
3 (:import (javax.sound.midi MidiSystem
4 Sequence
5 Track
6 MidiEvent
7 MetaMessage
9 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 MANIPULATION
21 (defn midi-load
22 "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-file
33 "Plays the MIDI file at the given location. The MIDI file runs in
34 the current thread until it finishes or is cancelled."
35 (comp midi-play-seq midi-load))
38 (defn midi-play-seq
39 "Plays the MIDI Sequence. The MIDI runs in
40 the current thread until it finishes or is cancelled."
41 [midi]
42 (if (nil? midi) nil
43 (let [song
44 (doto
45 (MidiSystem/getSequencer)
46 (.open)
47 (.setSequence midi)
48 (.start))]
49 (try
50 (loop []
51 (if (. song (isRunning))
52 (do
53 (Thread/sleep 10)
54 (recur))
55 ))
56 (finally (.close song))))))
59 (defn midi-test-1 []
60 (-> (.
61 (midi-load
62 "/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 (.getMessage
74 %))) #(.getMessage %) ) evts)
75 )
77 )))
83 (defn midi-short
84 "Creates a MIDI event containing a ShortMessage."
85 [tick [status & ns]]
86 (MidiEvent.
87 (apply
88 (fn
89 ([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 status
94 ns)
95 tick))
97 (defn midi-meta
98 "Creates a MIDI event containing a MetaMessage"
99 [tick type ns]
100 (MidiEvent.
101 (doto (MetaMessage.)
102 (.setMessage type
103 (byte-array (map byte ns))
104 (count ns)))
105 tick))
109 (defn sign
110 "Interpret the bits of n as a signed two's-complement byte"
111 [n]
112 (if (>= n 128) (- n 256)
113 n))
115 (defn unsign
116 "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 beat
125 (doto (. sequence (createTrack))
126 (.add (midi-meta 0 3 [-1 3 33 79 114 105 103 105 110 97 108 32
127 99 111 109 112 111 115 101 114 58 32 74 117 110 105 99 104 105 32 77
128 97 115 117 100 97]))
130 (.add (midi-short 0 [-80 0 0])) ;; control change = -80
131 (.add (midi-short 0 [-80 7 100])) ;; control change, volume, 100
132 (.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 0
134 (.add (midi-short 0 [-64 01])) ;; program/instrument change = -64
135 (.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?! = -32
140 (.add (midi-short 3 [-112 68 100])) ;; note on = -112
141 (.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 change
147 (.add (midi-short 120 [-112 76 100])) ;; note-on
148 (.add (midi-short 181 [-80 7 90])) ;; control change
149 (.add (midi-short 209 [-80 7 80])) ;; control change
150 (.add (midi-short 240 [-80 7 65])) ;; control change
151 (.add (midi-short 270 [-80 7 50])) ;; control change
152 (.add (midi-short 300 [-112 76 0])) ;; note on = -112
153 (.add (midi-short 360 [-80 7 0])))
154 ;;(.add (midi-short 360 [-1 47 0])) ;; system reset = -1
156 sequence
157 )))
162 ;;; ROM MUSIC MANIPULATION
164 (def songs;; music-headers
165 {
166 :pallet 0x822E
167 :pkmn-center 0x8237
168 :gym 0x8240
169 :city-1 0x8249 ;;virian, pewter, saffron
170 :city-2 0x8255 ;; cerulean, fuchsia
171 :celedon 0x825E
172 :cinnibar 0x8267
173 :vermilion 0x8270
174 :lavender 0x827C
175 :ss-anne 0x8288
176 :meet-prof 0x8291
177 :meet-blue 0x829A
178 :follow 0x82A3
179 :safari 0x82AF
180 :sfx-heal 0x82BA
181 :route-1 0x82C1 ;; route 1,2
182 :route-2 0x82CD ;; route 24, 25
183 :route-3 0x82D9 ;; route 3-10,16-22
184 :route-4 0x82E5 ;; route 11-15
185 :route-5 0x82F1 ;; indigo plateau
187 :title 0x7C249
188 :credits 0x7C255
189 :hall-of-fame 0x7C25E
190 :lab-prof 0x7C267
191 :jigglypuff 0x7C270
192 :bike 0x7C276
193 :surfing 0x7C282
194 :casino 0x7C28B
195 :intro-battle 0x7C294
196 :power-plant 0x7C2A0 ;; power plant, unknown dungeon
197 :viridian-forest 0x7C2AC ;;viridian forest, seafoam islands
198 :victory-rd 0x7C2B8 ;;mt moon, rock tunnel, victory rd
199 :mansion 0x7C2C4
200 :pkmn-tower 0x7C2D0
201 :silph 0x7C2D9
202 :trainer-bad 0x7C2E2
203 :trainer-girl 0x7C2EB
204 :trainer-angry 0x7C2F4
205 })
208 })
211 (defn low-high-format
212 "Returns the number represented by the bytes."
213 [low high]
214 (+ low (* 256 high)))
215 (defn high-low-format
216 "Returns the number represented by the bytes."
217 [high low]
218 (+ low (* 256 high)))
221 (defn rom-tracks
222 "Given a valid address to a music header, returns a list of the
223 data tracks"
224 [address]
225 (let [rom (rom (root))
226 tracklist
227 ((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 1
236 )]
238 tracklist
239 (map
240 (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-ops
253 "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) midi
259 (= 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 beat
269 (.createTrack))
270 ops
271 ))
279 ;; 8237-823F Pokecenter
280 ;; 8240-8248 Gym
281 ;; 8249-8254 Viridian / Pewter / Saffron
282 ;; 8255-825D Cerulean / Fuchsia
283 ;; 825E-8266 Celedon
284 ;; 8267-826F Cinnibar
285 ;; 8270-827B Vermilion
286 ;; 827C-8287 Lavender
287 ;; 8288-8290 S.S. Anne
288 ;; 8291-8299 Meet Prof. Oak
289 ;; 829A-82A2 Meet Rival
290 ;; 82A3-82AE Guy Walks you to Museum
291 ;; 82AF-82B7 Safari Zone
292 ;; 82B8-82C0 Pokemon get healed
293 ;; 82C1-82CC Routes 1 / 2
294 ;; 82CD-82D8 Routes 24 / 25
295 ;; 82D9-82E4 Routes 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 16 / 17 / 18 / 19 / 20 / 21 / 22
296 ;; 82E5-82F0 Routes 11 / 12 / 13 / 14 / 15
297 ;; 82F1-82FD Route 23 / Indigo Plateau
298 ;; 7C249-7C254 Title Screen
299 ;; 7C255-7C25D Credits
300 ;; 7C25E-7C266 Hall of FAme Registration
301 ;; 7C267-7C26F PRof Oak's LAb
302 ;; 7C270-7C275 Jigglypuff's Song
303 ;; 7C276-7C281 Bike Riding
304 ;; 7C282-7C28A Surfing
305 ;; 7C28B-7C293 Casino
306 ;; 7C294-7C29F Introduction Battle
307 ;; 7C2A0-7C2AB Power Plant / Unknown Dungeon
308 ;; 7C2AC-7C2B7 Viridian Forest / Seafoam Islands
309 ;; 7C2B8-7C2C3 Mt. Moon / Rock Tunnel / Victory Road
310 ;; 7C2C4-7C2CF Cinnibar Mansion
311 ;; 7C2D0-7C2D8 Pokemon Tower
312 ;; 7C2D9-7C2E1 Silph Co
313 ;; 7C2E2-7C2EA Meet Bad Trainer
314 ;; 7C2EB-7C2F3 Meet Girl Trainer
315 ;; 7C2F4-7C2FC Meet Angry Trainer