Mercurial > vba-clojure
diff clojure/com/aurellem/run/music.clj @ 438:067ea3f0d951
can now play midi files with two tracks.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Wed, 25 Apr 2012 13:46:31 -0500 |
parents | 20a9d5faf47c |
children | bf87b87a4ad7 |
line wrap: on
line diff
1.1 --- a/clojure/com/aurellem/run/music.clj Wed Apr 25 13:09:06 2012 -0500 1.2 +++ b/clojure/com/aurellem/run/music.clj Wed Apr 25 13:46:31 2012 -0500 1.3 @@ -390,27 +390,29 @@ 1.4 :duration length 1.5 :volume 0}) 1.6 1.7 -(defn midi->mini-midi [#^File midi-file] 1.8 +(defn commands 1.9 + "return all events where #(= (:command %) command)" 1.10 + [command s] 1.11 + (filter #(= command (:command %)) s)) 1.12 + 1.13 +(defn midi-track->mini-midi [#^File midi-file track-num] 1.14 (let [midi-events (parse-midi midi-file) 1.15 1.16 - note-on-events 1.17 - (filter #(= :Note_on_c (:command %)) midi-events) 1.18 - note-off-events 1.19 - (filter #(= :Note_off_c (:command %)) midi-events) 1.20 + note-on-events (commands :Note_on_c midi-events) 1.21 + note-off-events (commands :Note_off_c midi-events) 1.22 1.23 - channel-1-on 1.24 - (sort-by :time 1.25 - (filter #(= 1 (:channel (:args %))) 1.26 - note-on-events)) 1.27 - channel-1-off 1.28 - (sort-by :time 1.29 - (filter #(= 1 (:channel (:args %))) 1.30 - note-off-events)) 1.31 + select-channel 1.32 + (fn [n s] 1.33 + (sort-by :time (filter #(= n (:channel (:args %))) s))) 1.34 + 1.35 + channel-on (select-channel track-num note-on-events) 1.36 1.37 + channel-off (select-channel track-num note-off-events) 1.38 1.39 - tempo (:args (first (filter #(= :Tempo (:command %)) midi-events))) 1.40 - division (:division 1.41 - (:args (first (filter #(= :Header (:command %)) midi-events)))) 1.42 + 1.43 + tempo (:args (first (commands :Tempo midi-events))) 1.44 + division 1.45 + (:division (:args (first (commands :Header midi-events)))) 1.46 1.47 notes 1.48 (map 1.49 @@ -423,7 +425,7 @@ 1.50 :volume (int (/ (:velocity (:args note-on)) 10)) 1.51 :time-stamp (/ (* (/ tempo division) 1.52 (:time note-on)) 1e6)}) 1.53 - channel-1-on channel-1-off) 1.54 + channel-on channel-off) 1.55 1.56 silences 1.57 (map (fn [note-1 note-2] 1.58 @@ -437,9 +439,8 @@ 1.59 notes) 1.60 1.61 notes-with-silence 1.62 - (filter (comp not zero? :duration) (interleave silences notes)) 1.63 - ] 1.64 - 1.65 + (filter (comp not zero? :duration) 1.66 + (interleave silences notes))] 1.67 (map 1.68 (fn [note-event] 1.69 (note-codes (:frequency note-event) 1.70 @@ -447,6 +448,27 @@ 1.71 (int (* (:duration note-event) 0x100)))) 1.72 notes-with-silence))) 1.73 1.74 +(defn midi->mini-midi [#^File midi-file] 1.75 + {:track-1 (flatten (midi-track->mini-midi midi-file 1)) 1.76 + :track-2 (flatten (midi-track->mini-midi midi-file 2))}) 1.77 + 1.78 +(defn play-midi [#^File midi-file] 1.79 + (let [track-1-target 0xA000 1.80 + track-2-target 0xB000 1.81 + program-target 0xC000 1.82 + mini-midi (midi->mini-midi midi-file) 1.83 + long-silence (flatten (note-codes 20 0 9001))] 1.84 + 1.85 + (-> (second (music-base)) 1.86 + (set-memory-range track-1-target long-silence) 1.87 + (set-memory-range track-2-target long-silence) 1.88 + (set-memory-range track-1-target (:track-1 mini-midi)) 1.89 + (set-memory-range track-2-target (:track-2 mini-midi)) 1.90 + (set-memory-range program-target (music-kernel)) 1.91 + (PC! program-target)))) 1.92 + 1.93 + 1.94 + 1.95 1.96 (def C4 (partial note-codes 261.63)) 1.97 (def D4 (partial note-codes 293.66)) 1.98 @@ -477,6 +499,7 @@ 1.99 (PC! program-target)))) 1.100 1.101 1.102 + 1.103 ;; (defn test-note [music-bytes] 1.104 ;; (-> (set-memory-range (second (music-base)) 1.105 ;; 0xC000 (concat (clear-music-registers)