# HG changeset patch # User Robert McIntyre # Date 1335379591 18000 # Node ID 067ea3f0d9514e3d9b05ebbd4e84e4da7f1b65b8 # Parent 20a9d5faf47cfed80b80923eecf1fd6db2ae3152 can now play midi files with two tracks. diff -r 20a9d5faf47c -r 067ea3f0d951 clojure/com/aurellem/run/music.clj --- a/clojure/com/aurellem/run/music.clj Wed Apr 25 13:09:06 2012 -0500 +++ b/clojure/com/aurellem/run/music.clj Wed Apr 25 13:46:31 2012 -0500 @@ -390,27 +390,29 @@ :duration length :volume 0}) -(defn midi->mini-midi [#^File midi-file] +(defn commands + "return all events where #(= (:command %) command)" + [command s] + (filter #(= command (:command %)) s)) + +(defn midi-track->mini-midi [#^File midi-file track-num] (let [midi-events (parse-midi midi-file) - note-on-events - (filter #(= :Note_on_c (:command %)) midi-events) - note-off-events - (filter #(= :Note_off_c (:command %)) midi-events) + note-on-events (commands :Note_on_c midi-events) + note-off-events (commands :Note_off_c midi-events) - channel-1-on - (sort-by :time - (filter #(= 1 (:channel (:args %))) - note-on-events)) - channel-1-off - (sort-by :time - (filter #(= 1 (:channel (:args %))) - note-off-events)) + select-channel + (fn [n s] + (sort-by :time (filter #(= n (:channel (:args %))) s))) + + channel-on (select-channel track-num note-on-events) + channel-off (select-channel track-num note-off-events) - tempo (:args (first (filter #(= :Tempo (:command %)) midi-events))) - division (:division - (:args (first (filter #(= :Header (:command %)) midi-events)))) + + tempo (:args (first (commands :Tempo midi-events))) + division + (:division (:args (first (commands :Header midi-events)))) notes (map @@ -423,7 +425,7 @@ :volume (int (/ (:velocity (:args note-on)) 10)) :time-stamp (/ (* (/ tempo division) (:time note-on)) 1e6)}) - channel-1-on channel-1-off) + channel-on channel-off) silences (map (fn [note-1 note-2] @@ -437,9 +439,8 @@ notes) notes-with-silence - (filter (comp not zero? :duration) (interleave silences notes)) - ] - + (filter (comp not zero? :duration) + (interleave silences notes))] (map (fn [note-event] (note-codes (:frequency note-event) @@ -447,6 +448,27 @@ (int (* (:duration note-event) 0x100)))) notes-with-silence))) +(defn midi->mini-midi [#^File midi-file] + {:track-1 (flatten (midi-track->mini-midi midi-file 1)) + :track-2 (flatten (midi-track->mini-midi midi-file 2))}) + +(defn play-midi [#^File midi-file] + (let [track-1-target 0xA000 + track-2-target 0xB000 + program-target 0xC000 + mini-midi (midi->mini-midi midi-file) + long-silence (flatten (note-codes 20 0 9001))] + + (-> (second (music-base)) + (set-memory-range track-1-target long-silence) + (set-memory-range track-2-target long-silence) + (set-memory-range track-1-target (:track-1 mini-midi)) + (set-memory-range track-2-target (:track-2 mini-midi)) + (set-memory-range program-target (music-kernel)) + (PC! program-target)))) + + + (def C4 (partial note-codes 261.63)) (def D4 (partial note-codes 293.66)) @@ -477,6 +499,7 @@ (PC! program-target)))) + ;; (defn test-note [music-bytes] ;; (-> (set-memory-range (second (music-base)) ;; 0xC000 (concat (clear-music-registers)