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@242: (:use (com.aurellem.gb saves 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@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@242: (defn midi-play ocsenave@242: "Plays the MIDI file at the given location. The MIDI file runs in ocsenave@242: the current thread until it finishes or is cancelled." ocsenave@242: [path] ocsenave@242: (let [midi (midi-load path)] 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@242: (throw (Exception. "Song stopped!")))) ocsenave@242: (finally (.close song))))))) ocsenave@242: ocsenave@242: 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@242: (second) ocsenave@242: ocsenave@242: ((fn[trk] ocsenave@242: (map #(. trk (get %)) ocsenave@242: (range 0 (. trk size))))) ocsenave@242: ocsenave@242: ((fn [evts] ocsenave@242: (map (juxt #(identity (.getMessage %)) #(vec (.getMessage (.getMessage %))) #(.getTick %) ) evts) ocsenave@242: ) ocsenave@242: ocsenave@242: ))) ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: (defn midi-event ocsenave@242: "Creates an event at the given tick, of the given msg-type, ocsenave@242: containing data in coll. msg-type can be :meta, :short. If :meta, x ocsenave@242: is the type of the msg. If :short, x is the status of the message." ocsenave@242: [tick msg-type x coll] ocsenave@242: ocsenave@242: (cond (= msg-type :meta) ocsenave@242: (MetaMessage.) ocsenave@242: (= msg-type :short) ocsenave@242: (ShortMessage.)) ocsenave@242: ocsenave@242: ) ocsenave@242: ocsenave@242: ocsenave@242: (defn midi-test-2 [] ocsenave@242: (let [sequence (Sequence. (float 30) 15)] ;; 30 fps, 15 frames per beat ocsenave@242: (doto (. sequence (createTrack)) ocsenave@242: (.add (MidiEvent. (MetaMessage.) 0))))) ocsenave@242: ocsenave@242: ocsenave@242: ocsenave@242: [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]) ocsenave@242: com.aurellem.music.midi-util> (midi-test-1) ocsenave@242: ([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]) ocsenave@242: 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@242: :safari 0x82AF ocsenave@242: :sfx-heal 0x82BA 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@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@242: :intro-battle 0x7C294 ocsenave@242: :power-plant 0x7C2A0 ;; power plant, unknown dungeon ocsenave@242: :viridian-forest 0x7C2AC ;;viridian forest, seafoam islands ocsenave@242: :victory-rd 0x7C2B8 ;;mt moon, rock tunnel, victory rd ocsenave@242: :mansion 0x7C2C4 ocsenave@242: :pkmn-tower 0x7C2D0 ocsenave@242: :silph 0x7C2D9 ocsenave@242: :trainer-bad 0x7C2E2 ocsenave@242: :trainer-girl 0x7C2EB ocsenave@242: :trainer-angry 0x7C2F4 ocsenave@242: }) ocsenave@242: ocsenave@242: 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@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@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@242: (defn parse-track ocsenave@242: "Consumes the list of opcodes, returning a runnable MIDI Sequence object." ocsenave@242: [track] ocsenave@242: (fn [midi track] ocsenave@242: (cond (empty? track) midi))) ocsenave@242: ocsenave@242: ocsenave@242: 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