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@243: ocsenave@243: (def midi-play-file 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@243: (comp midi-play-seq midi-load)) ocsenave@243: 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@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@243: ))) ocsenave@242: ocsenave@243: 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@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@243: ocsenave@243: (defn note? ocsenave@243: "Does the given byte correspond to a note?" ocsenave@243: [n] ocsenave@243: ocsenave@243: (defn parse-ops ocsenave@242: "Consumes the list of opcodes, returning a runnable MIDI Sequence object." ocsenave@243: [ops] ocsenave@243: ( ocsenave@243: (fn [midi ops] ocsenave@243: (let [x (first ops)] ocsenave@243: (cond (empty? ops) midi ocsenave@243: (= x 0xDA) ocsenave@243: ;; set tempo (high-low (nth ops 1)(nth ops 2)) ocsenave@243: (recur (identity midi) (drop 3 ops)) ocsenave@243: ocsenave@243: (note? x) ocsenave@243: ocsenave@242: ocsenave@243: ) ocsenave@242: ocsenave@243: (doto (Sequence. (float 30) 15) ;; 30 fps, 15 frames per beat ocsenave@243: (.createTrack)) ocsenave@243: ops ocsenave@243: )) 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