ocsenave@242: (ns com.aurellem.music.midi-util ocsenave@242: ;;(:import javax.sound.sampled) ocsenave@242: (:import (javax.sound.midi MidiSystem ocsenave@242: Sequence ocsenave@242: Track ocsenave@242: MidiEvent ocsenave@242: MetaMessage ocsenave@242: ocsenave@242: ShortMessage) ocsenave@242: (com.sun.media.sound FastShortMessage) ocsenave@242: (java.io File)) ocsenave@242: ocsenave@309: (:use (com.aurellem.gb saves util mem-util constants gb-driver vbm items assembly characters)) ocsenave@242: (:use (com.aurellem.run title)) ocsenave@242: (:use (com.aurellem.exp pokemon item-bridge)) ocsenave@309: ;; (:use (com.aurellem.world practice)) ocsenave@242: (:import [com.aurellem.gb.gb_driver SaveState])) ocsenave@242: ocsenave@242: ocsenave@242: ;;; PURE MIDI MANIPULATION ocsenave@242: ocsenave@242: (defn midi-load ocsenave@242: "Takes a path to a MIDI file and returns a Sequence object." ocsenave@242: [path] ocsenave@242: ((fn [file] ocsenave@242: (if (.exists file) ocsenave@242: (MidiSystem/getSequence file) ocsenave@242: nil)) ocsenave@242: (File. path))) ocsenave@242: ocsenave@242: ocsenave@243: ocsenave@243: (defn midi-play-seq ocsenave@243: "Plays the MIDI Sequence. The MIDI runs in ocsenave@243: the current thread until it finishes or is cancelled." ocsenave@243: [midi] ocsenave@242: (if (nil? midi) nil ocsenave@242: (let [song ocsenave@242: (doto ocsenave@242: (MidiSystem/getSequencer) ocsenave@242: (.open) ocsenave@242: (.setSequence midi) ocsenave@242: (.start))] ocsenave@242: (try ocsenave@242: (loop [] ocsenave@242: (if (. song (isRunning)) ocsenave@242: (do ocsenave@242: (Thread/sleep 10) ocsenave@242: (recur)) ocsenave@243: )) ocsenave@243: (finally (.close song)))))) ocsenave@242: ocsenave@242: ocsenave@306: (def midi-play-file ocsenave@306: "Plays the MIDI file at the given location. The MIDI file runs in ocsenave@306: the current thread until it finishes or is cancelled." ocsenave@306: (comp midi-play-seq midi-load)) ocsenave@306: ocsenave@306: ocsenave@306: ocsenave@306: ocsenave@242: (defn midi-test-1 [] ocsenave@242: (-> (. ocsenave@242: (midi-load ocsenave@242: "/home/ocsenave/bk_robert/sounds/sounds/www.vgmusic.com/console/nintendo/gameboy/PkmRB-Item.mid") ocsenave@242: (getTracks)) ocsenave@242: ocsenave@242: (vec) ocsenave@243: (nth 1) ocsenave@242: ocsenave@242: ((fn[trk] ocsenave@242: (map #(. trk (get %)) ocsenave@242: (range 0 (. trk size))))) ocsenave@242: ocsenave@242: ((fn [evts] ocsenave@243: (map (juxt #(.getTick %) #(vec (.getMessage (.getMessage ocsenave@243: %))) #(.getMessage %) ) evts) ocsenave@242: ) ocsenave@242: ocsenave@242: ))) ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@243: (defn midi-short ocsenave@243: "Creates a MIDI event containing a ShortMessage." ocsenave@243: [tick [status & ns]] ocsenave@243: (MidiEvent. ocsenave@243: (apply ocsenave@243: (fn ocsenave@243: ([x] (doto (ShortMessage.) (.setMessage x))) ocsenave@243: ([x y] (doto (ShortMessage.) (.setMessage x y 0))) ocsenave@243: ([x y z] (doto (ShortMessage.) (.setMessage x y z))) ocsenave@243: ([x y z w] (doto (ShortMessage.) (.setMessage x y z w)))) ocsenave@243: status ocsenave@243: ns) ocsenave@243: tick)) ocsenave@242: ocsenave@243: (defn midi-meta ocsenave@243: "Creates a MIDI event containing a MetaMessage" ocsenave@243: [tick type ns] ocsenave@243: (MidiEvent. ocsenave@243: (doto (MetaMessage.) ocsenave@243: (.setMessage type ocsenave@243: (byte-array (map byte ns)) ocsenave@243: (count ns))) ocsenave@243: tick)) ocsenave@242: ocsenave@243: ocsenave@243: ocsenave@243: (defn sign ocsenave@243: "Interpret the bits of n as a signed two's-complement byte" ocsenave@243: [n] ocsenave@243: (if (>= n 128) (- n 256) ocsenave@243: n)) ocsenave@243: ocsenave@243: (defn unsign ocsenave@243: "Interpret the bits as an unsigned byte." ocsenave@243: [n] ocsenave@243: (if (neg? n) (+ n 256) n)) ocsenave@243: ocsenave@243: ocsenave@242: ocsenave@242: ocsenave@242: (defn midi-test-2 [] ocsenave@243: (let [sequence (Sequence. (float 30) 12)] ;; 30 fps, 10 frames per beat ocsenave@242: (doto (. sequence (createTrack)) ocsenave@243: (.add (midi-meta 0 3 [-1 3 33 79 114 105 103 105 110 97 108 32 ocsenave@243: 99 111 109 112 111 115 101 114 58 32 74 117 110 105 99 104 105 32 77 ocsenave@243: 97 115 117 100 97])) ocsenave@242: ocsenave@243: (.add (midi-short 0 [-80 0 0])) ;; control change = -80 ocsenave@243: (.add (midi-short 0 [-80 7 100])) ;; control change, volume, 100 ocsenave@243: (.add (midi-short 0 [-80 10 64])) ;; control change, pan, 64 (middle?) ocsenave@243: (.add (midi-short 0 [-80 32 0])) ;; ctrl chg, LSB ctrl 0 = bank 0 ocsenave@243: (.add (midi-short 0 [-64 01])) ;; program/instrument change = -64 ocsenave@243: (.add (midi-short 0 [-80 101 0])) ocsenave@243: (.add (midi-short 1 [-80 100 0])) ocsenave@243: (.add (midi-short 2 [-80 6 2])) ocsenave@243: (.add (midi-short 3 [-80 38 0])) ocsenave@243: (.add (midi-short 3 [-32 0 56])) ;; pitch bend?! = -32 ocsenave@243: (.add (midi-short 3 [-112 68 100])) ;; note on = -112 ocsenave@243: (.add (midi-short 20 [-112 68 0])) ocsenave@243: (.add (midi-short 40 [-112 68 100])) ocsenave@243: (.add (midi-short 60 [-112 68 0])) ocsenave@243: (.add (midi-short 80 [-112 68 100])) ocsenave@243: (.add (midi-short 100 [-112 68 0])) ocsenave@243: (.add (midi-short 120 [-80 7 100])) ;; control change ocsenave@243: (.add (midi-short 120 [-112 76 100])) ;; note-on ocsenave@243: (.add (midi-short 181 [-80 7 90])) ;; control change ocsenave@243: (.add (midi-short 209 [-80 7 80])) ;; control change ocsenave@243: (.add (midi-short 240 [-80 7 65])) ;; control change ocsenave@243: (.add (midi-short 270 [-80 7 50])) ;; control change ocsenave@243: (.add (midi-short 300 [-112 76 0])) ;; note on = -112 ocsenave@243: (.add (midi-short 360 [-80 7 0]))) ocsenave@243: ;;(.add (midi-short 360 [-1 47 0])) ;; system reset = -1 ocsenave@242: ocsenave@243: sequence ocsenave@306: )) ocsenave@242: ocsenave@243: ocsenave@306: ocsenave@306: ocsenave@306: (defn read-memory ocsenave@306: ([mem start length] ocsenave@306: (take length ocsenave@306: (drop start ocsenave@306: mem))) ocsenave@306: ([start length] ocsenave@306: (read-memory (rom(root)) start length))) ocsenave@242: ocsenave@242: ;;; ROM MUSIC MANIPULATION ocsenave@242: ocsenave@242: (def songs;; music-headers ocsenave@242: { ocsenave@242: :pallet 0x822E ocsenave@242: :pkmn-center 0x8237 ocsenave@242: :gym 0x8240 ocsenave@242: :city-1 0x8249 ;;virian, pewter, saffron ocsenave@242: :city-2 0x8255 ;; cerulean, fuchsia ocsenave@242: :celedon 0x825E ocsenave@242: :cinnibar 0x8267 ocsenave@242: :vermilion 0x8270 ocsenave@242: :lavender 0x827C ocsenave@242: :ss-anne 0x8288 ocsenave@242: :meet-prof 0x8291 ocsenave@242: :meet-blue 0x829A ocsenave@242: :follow 0x82A3 ocsenave@309: :evolution 0x82AF ocsenave@309: :sfx-heal 0x82B8 ocsenave@242: :route-1 0x82C1 ;; route 1,2 ocsenave@242: :route-2 0x82CD ;; route 24, 25 ocsenave@242: :route-3 0x82D9 ;; route 3-10,16-22 ocsenave@242: :route-4 0x82E5 ;; route 11-15 ocsenave@242: :route-5 0x82F1 ;; indigo plateau ocsenave@242: ocsenave@309: ;;:1 0xc977 ;; fourteen "tracks" X ocsenave@309: :1 0x801cb ;; slot machine music ocsenave@309: :2 0x801d4 ocsenave@309: :3 0x801dd ocsenave@309: ocsenave@309: :4 0x202be ;; uber slow battle songs? ocsenave@309: :5 0x202c7 ;; can't play with pallet or bike (wrong base) ocsenave@309: :6 0x202d9 ocsenave@309: :7 0x202e2 ocsenave@309: :8 0x202eb ocsenave@309: :9 0x202f4 ocsenave@309: ocsenave@242: :title 0x7C249 ocsenave@242: :credits 0x7C255 ocsenave@242: :hall-of-fame 0x7C25E ocsenave@242: :lab-prof 0x7C267 ocsenave@242: :jigglypuff 0x7C270 ocsenave@242: :bike 0x7C276 ocsenave@242: :surfing 0x7C282 ocsenave@242: :casino 0x7C28B ocsenave@309: :intro 0x7C294 ocsenave@309: :power-plant 0x7C29D ;; power plant, unknown dungeon ocsenave@309: :viridian-forest 0x7C2A9 ;;viridian forest, seafoam islands ocsenave@309: :victory-rd 0x7C2B5 ;;mt moon, rock tunnel, victory rd ocsenave@309: :mansion 0x7C2C1 ocsenave@309: :pkmn-tower 0x7C2CD ocsenave@309: :silph 0x7C2D6 ocsenave@309: :trainer-bad 0x7C2DF ocsenave@309: :trainer-girl 0x7C2E8 ocsenave@309: :trainer-angry 0x7C2F1 ocsenave@242: }) ocsenave@242: ocsenave@242: ocsenave@242: (defn low-high-format ocsenave@242: "Returns the number represented by the bytes." ocsenave@242: [low high] ocsenave@242: (+ low (* 256 high))) ocsenave@243: (defn high-low-format ocsenave@243: "Returns the number represented by the bytes." ocsenave@243: [high low] ocsenave@243: (+ low (* 256 high))) ocsenave@243: ocsenave@242: ocsenave@242: (defn rom-tracks ocsenave@242: "Given a valid address to a music header, returns a list of the ocsenave@242: data tracks" ocsenave@242: [address] ocsenave@242: (let [rom (rom (root)) ocsenave@242: tracklist ocsenave@242: ((fn extract-tracklist [mem n] ocsenave@242: (if (= (nth mem 2) n) ocsenave@242: (cons (low-high-format (first mem) ocsenave@242: (second mem)) ocsenave@242: (extract-tracklist (drop 3 mem) (inc n))) ocsenave@242: '())) ocsenave@242: ocsenave@242: (take 12 (drop (inc address) rom)) ocsenave@242: 1 ocsenave@242: )] ocsenave@242: ocsenave@243: tracklist ocsenave@242: (map ocsenave@242: (fn [trk] (take-while #(not= 0xFF %) (drop trk rom))) ocsenave@242: tracklist) ocsenave@242: ocsenave@242: )) ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@309: (defn find-music [] ocsenave@309: (let [extract-song ocsenave@309: (fn [mem] ocsenave@309: (if (and ocsenave@309: (> (count mem) 6) ocsenave@309: (zero? (rem (first mem) 16)) ocsenave@309: (not(zero? (first mem))) ocsenave@309: (= (nth mem 3) 1) ocsenave@309: (= (nth mem 6) 2)) ocsenave@309: (take 12 mem)))] ocsenave@309: (loop [mem (rom) ocsenave@309: results [] ocsenave@309: ptr 0] ocsenave@309: (cond (empty? mem) ocsenave@309: results ocsenave@243: ocsenave@309: (nil? (extract-song mem)) ocsenave@309: (recur (rest mem) ocsenave@309: results ocsenave@309: (inc ptr)) ocsenave@309: :else ocsenave@309: (recur (rest mem) ocsenave@309: (conj results ocsenave@309: [(hex ptr) (extract-song mem)]) ocsenave@309: (inc ptr)))))) ocsenave@309: ocsenave@309: (defn music-header ocsenave@309: "Given a valid address to a music header, returns the music header." ocsenave@309: [address] ocsenave@309: (let [ ocsenave@309: rom (rom) ocsenave@309: data (drop address rom) ocsenave@309: ] ocsenave@309: (-> ocsenave@309: (loop [n 1 k 3] ocsenave@309: (if (= n (nth data k)) ocsenave@309: (recur (inc n) (+ k 3)) ocsenave@309: k)) ocsenave@309: (take data)))) ocsenave@309: ocsenave@309: (defn pallet-song [song-name] ocsenave@309: (write-rom! ocsenave@309: (rewrite-memory ocsenave@309: (vec(rom)) ocsenave@309: 0x822e ocsenave@309: (music-header (songs song-name))))) ocsenave@309: ocsenave@309: (defn bike-song [song-name] ocsenave@309: (write-rom! ocsenave@309: (rewrite-memory ocsenave@309: (vec(rom)) ocsenave@309: 0x7c276 ocsenave@309: (music-header (songs song-name))))) ocsenave@309: ocsenave@309: (defn bike-song* [address] ocsenave@309: (write-rom! ocsenave@309: (rewrite-memory ocsenave@309: (vec(rom)) ocsenave@309: 0x7c276 ocsenave@309: (music-header address)))) ocsenave@309: ocsenave@309: ocsenave@309: (defn pallet-song* [address] ocsenave@309: (write-rom! ocsenave@309: (rewrite-memory ocsenave@309: (vec(rom)) ocsenave@309: 0x822e ocsenave@309: (music-header address)))) ocsenave@306: ;; (defn note? ocsenave@306: ;; "Does the given byte correspond to a note?" ocsenave@306: ;; [n]) ocsenave@243: ocsenave@306: ;; (comment defn parse-ops ocsenave@306: ;; "Consumes the list of opcodes, returning a runnable MIDI Sequence object." ocsenave@306: ;; [ops] ocsenave@306: ;; ( ocsenave@306: ;; (fn [midi ops] ocsenave@306: ;; (let [x (first ops)] ocsenave@306: ;; (cond (empty? ops) midi ocsenave@306: ;; (= x 0xDA) ocsenave@306: ;; ;; set tempo (high-low (nth ops 1)(nth ops 2)) ocsenave@306: ;; (recur (identity midi) (drop 3 ops)) ocsenave@243: ocsenave@306: ;; (note? x) ocsenave@243: ocsenave@242: ocsenave@306: ;; ) ocsenave@242: ocsenave@306: ;; (doto (Sequence. (float 30) 15) ;; 30 fps, 15 frames per beat ocsenave@306: ;; (.createTrack)) ocsenave@306: ;; ops ocsenave@306: ;; )))) ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ;; 8237-823F Pokecenter ocsenave@242: ;; 8240-8248 Gym ocsenave@242: ;; 8249-8254 Viridian / Pewter / Saffron ocsenave@242: ;; 8255-825D Cerulean / Fuchsia ocsenave@242: ;; 825E-8266 Celedon ocsenave@242: ;; 8267-826F Cinnibar ocsenave@242: ;; 8270-827B Vermilion ocsenave@242: ;; 827C-8287 Lavender ocsenave@242: ;; 8288-8290 S.S. Anne ocsenave@242: ;; 8291-8299 Meet Prof. Oak ocsenave@242: ;; 829A-82A2 Meet Rival ocsenave@242: ;; 82A3-82AE Guy Walks you to Museum ocsenave@242: ;; 82AF-82B7 Safari Zone ocsenave@242: ;; 82B8-82C0 Pokemon get healed ocsenave@242: ;; 82C1-82CC Routes 1 / 2 ocsenave@242: ;; 82CD-82D8 Routes 24 / 25 ocsenave@242: ;; 82D9-82E4 Routes 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 16 / 17 / 18 / 19 / 20 / 21 / 22 ocsenave@242: ;; 82E5-82F0 Routes 11 / 12 / 13 / 14 / 15 ocsenave@242: ;; 82F1-82FD Route 23 / Indigo Plateau ocsenave@242: ;; 7C249-7C254 Title Screen ocsenave@242: ;; 7C255-7C25D Credits ocsenave@242: ;; 7C25E-7C266 Hall of FAme Registration ocsenave@242: ;; 7C267-7C26F PRof Oak's LAb ocsenave@242: ;; 7C270-7C275 Jigglypuff's Song ocsenave@242: ;; 7C276-7C281 Bike Riding ocsenave@242: ;; 7C282-7C28A Surfing ocsenave@242: ;; 7C28B-7C293 Casino ocsenave@242: ;; 7C294-7C29F Introduction Battle ocsenave@242: ;; 7C2A0-7C2AB Power Plant / Unknown Dungeon ocsenave@242: ;; 7C2AC-7C2B7 Viridian Forest / Seafoam Islands ocsenave@242: ;; 7C2B8-7C2C3 Mt. Moon / Rock Tunnel / Victory Road ocsenave@242: ;; 7C2C4-7C2CF Cinnibar Mansion ocsenave@242: ;; 7C2D0-7C2D8 Pokemon Tower ocsenave@242: ;; 7C2D9-7C2E1 Silph Co ocsenave@242: ;; 7C2E2-7C2EA Meet Bad Trainer ocsenave@242: ;; 7C2EB-7C2F3 Meet Girl Trainer ocsenave@242: ;; 7C2F4-7C2FC Meet Angry Trainer rlm@445: