Mercurial > vba-clojure
comparison clojure/com/aurellem/run/music.clj @ 427:fbccf46cf34d
sucessfully played third-kind.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 23 Apr 2012 08:26:23 -0500 |
parents | c03f28aa98d9 |
children | 476f7da175a4 |
comparison
equal
deleted
inserted
replaced
426:c03f28aa98d9 | 427:fbccf46cf34d |
---|---|
6 bootstrap-0 bootstrap-1)) | 6 bootstrap-0 bootstrap-1)) |
7 (:require clojure.string) | 7 (:require clojure.string) |
8 (:import [com.aurellem.gb.gb_driver SaveState]) | 8 (:import [com.aurellem.gb.gb_driver SaveState]) |
9 (:import java.io.File)) | 9 (:import java.io.File)) |
10 | 10 |
11 | 11 (def third-kind |
12 (File. "/home/r/proj/midi/third-kind.mid")) | |
13 | |
14 (defn raw-midi-text [#^File midi-file] | |
15 (:out | |
16 (clojure.java.shell/sh | |
17 "midicsv" | |
18 (.getCanonicalPath midi-file) | |
19 "-"))) | |
20 | |
21 (def command-line #"^(\d+), (\d+), ([^,]+)(.*)$") | |
22 | |
23 (defmulti parse-command :command) | |
24 | |
25 (defn discard-args [command] (dissoc command :args)) | |
26 | |
27 (defmethod parse-command :Start_track | |
28 [command] (discard-args command)) | |
29 | |
30 (defmethod parse-command :End_track | |
31 [command] (discard-args command)) | |
32 | |
33 (defmethod parse-command :default | |
34 [command] command) | |
35 | |
36 (defn parse-number-list | |
37 [number-list-str] | |
38 (map #(Integer/parseInt %) | |
39 (clojure.string/split number-list-str #", "))) | |
40 | |
41 (defmethod parse-command :Tempo | |
42 [command] | |
43 (update-in command [:args] #(Integer/parseInt %))) | |
44 | |
45 (defn parse-midi-note-list | |
46 [midi-note-list-str] | |
47 (let [[channel note velocity] | |
48 (parse-number-list midi-note-list-str)] | |
49 {:channel channel :note note :velocity velocity})) | |
50 | |
51 (defmethod parse-command :Note_on_c | |
52 [command] | |
53 (update-in command [:args] parse-midi-note-list)) | |
54 | |
55 (defmethod parse-command :Note_off_c | |
56 [command] | |
57 (update-in command [:args] parse-midi-note-list)) | |
58 | |
59 (defmethod parse-command :Header | |
60 [command] | |
61 (let [args (:args command) | |
62 [format num-tracks division] (parse-number-list args)] | |
63 (assoc command :args | |
64 {:format format | |
65 :num-tracks num-tracks | |
66 :division division}))) | |
67 | |
68 (defmethod parse-command :Program_c | |
69 [command] | |
70 (let [args (:args command) | |
71 [channel program-num] (parse-number-list args)] | |
72 (assoc command :args | |
73 {:channel channel | |
74 :program-num program-num}))) | |
75 | |
76 (defn parse-midi [#^File midi-file] | |
77 (map | |
78 (comp parse-command | |
79 (fn [line] | |
80 (let [[[_ channel time command args]] | |
81 (re-seq command-line line)] | |
82 {:channel (Integer/parseInt channel) | |
83 :time (Integer/parseInt time) | |
84 :command (keyword command) | |
85 :args (apply str (drop 2 args))}))) | |
86 (drop-last | |
87 (clojure.string/split-lines | |
88 (raw-midi-text midi-file))))) | |
89 | |
12 (def music-base new-kernel) | 90 (def music-base new-kernel) |
13 | |
14 | |
15 | |
16 | 91 |
17 (defn store [n address] | 92 (defn store [n address] |
18 (flatten | 93 (flatten |
19 [0xF5 | 94 [0xF5 |
20 0xE5 | 95 0xE5 |
31 0xF1])) | 106 0xF1])) |
32 | 107 |
33 (defn infinite-loop [] | 108 (defn infinite-loop [] |
34 [0x18 0xFE]) | 109 [0x18 0xFE]) |
35 | 110 |
36 | |
37 | |
38 (def divider-register 0xFF04) | 111 (def divider-register 0xFF04) |
39 | |
40 | 112 |
41 (defrecord Bit-Note [frequency volume duration duty]) | 113 (defrecord Bit-Note [frequency volume duration duty]) |
42 | 114 |
43 (defn clear-music-registers [] | 115 (defn clear-music-registers [] |
44 (flatten | 116 (flatten |
90 ;; [new-duty] | 162 ;; [new-duty] |
91 | 163 |
92 (def note-code 0x00) | 164 (def note-code 0x00) |
93 (def change-duty-code 0x01) | 165 (def change-duty-code 0x01) |
94 (def silence-code 0x02) | 166 (def silence-code 0x02) |
95 | |
96 | 167 |
97 (defn do-message | 168 (defn do-message |
98 "Read the message which starts at the current value of HL and do | 169 "Read the message which starts at the current value of HL and do |
99 what it says. Duration is left in A, and HL is advanced | 170 what it says. Duration is left in A, and HL is advanced |
100 appropraitely." | 171 appropraitely." |
248 [note-code | 319 [note-code |
249 volume&high-frequency | 320 volume&high-frequency |
250 low-frequency | 321 low-frequency |
251 duration])) | 322 duration])) |
252 | 323 |
324 (defn midi-code->frequency | |
325 [midi-code] | |
326 (* 8.1757989156 | |
327 (Math/pow 2 (* (float (/ 12)) midi-code)))) | |
328 | |
329 ;; division == clock-pulses / quarter-note | |
330 ;; tempo == microseconds / quarter-note | |
331 | |
332 ;; have: clock-pulses | |
333 ;; want: seconds | |
334 | |
335 | |
336 | |
337 | |
338 (defn midi->mini-midi [#^File midi-file] | |
339 (let [midi-events (parse-midi midi-file) | |
340 | |
341 note-on-events | |
342 (filter #(= :Note_on_c (:command %)) midi-events) | |
343 note-off-events | |
344 (filter #(= :Note_off_c (:command %)) midi-events) | |
345 | |
346 channel-1-on | |
347 (sort-by :time | |
348 (filter #(= 1 (:channel (:args %))) | |
349 note-on-events)) | |
350 channel-1-off | |
351 (sort-by :time | |
352 (filter #(= 1 (:channel (:args %))) | |
353 note-off-events)) | |
354 | |
355 | |
356 tempo (:args (first (filter #(= :Tempo (:command %)) midi-events))) | |
357 division (:division | |
358 (:args (first (filter #(= :Header (:command %)) midi-events)))) | |
359 ] | |
360 | |
361 (map | |
362 (fn [note-event] | |
363 (note-codes (:frequency note-event) | |
364 (:volume note-event) | |
365 (int (* (:duration note-event) 0x100)))) | |
366 | |
367 (map | |
368 (fn [note-on note-off] | |
369 {:frequency (midi-code->frequency (:note (:args note-on))) | |
370 :duration | |
371 (/ (* (/ tempo division) | |
372 (- (:time note-off) (:time note-on))) | |
373 1e6) ;; convert clock-pulses into seconds | |
374 :volume (int (/ (:velocity (:args note-on)) 10)) | |
375 :time-stamp (/ (* (/ tempo division) | |
376 (:time note-on)) 1e6)}) | |
377 channel-1-on channel-1-off)))) | |
378 | |
379 | |
380 | |
381 | |
382 | |
383 | |
384 | |
253 (def C4 (partial note-codes 261.63)) | 385 (def C4 (partial note-codes 261.63)) |
254 (def D4 (partial note-codes 293.66)) | 386 (def D4 (partial note-codes 293.66)) |
255 (def E4 (partial note-codes 329.63)) | 387 (def E4 (partial note-codes 329.63)) |
256 (def F4 (partial note-codes 349.23)) | 388 (def F4 (partial note-codes 349.23)) |
257 (def G4 (partial note-codes 392)) | 389 (def G4 (partial note-codes 392)) |
312 (repeat | 444 (repeat |
313 500 | 445 500 |
314 [0xF0 | 446 [0xF0 |
315 0x05])])) | 447 0x05])])) |
316 | 448 |
317 (def third-kind | 449 |
318 (File. "/home/r/proj/midi/third-kind.mid")) | |
319 | |
320 (defn raw-midi-text [#^File midi-file] | |
321 (:out | |
322 (clojure.java.shell/sh | |
323 "midicsv" | |
324 (.getCanonicalPath midi-file) | |
325 "-"))) | |
326 | |
327 (def command-line #"^(\d+), (\d+), ([^,]+)(.*)$") | |
328 | |
329 (defmulti parse-command :command) | |
330 | |
331 (defn discard-args [command] (dissoc command :args)) | |
332 | |
333 (defmethod parse-command :Start_track | |
334 [command] (discard-args command)) | |
335 | |
336 (defmethod parse-command :End_track | |
337 [command] (discard-args command)) | |
338 | |
339 (defmethod parse-command :default | |
340 [command] command) | |
341 | |
342 (defn parse-number-list | |
343 [number-list-str] | |
344 (map #(Integer/parseInt %) | |
345 (clojure.string/split number-list-str #", "))) | |
346 | |
347 (defmethod parse-command :Tempo | |
348 [command] | |
349 (update-in command [:args] #(Integer/parseInt %))) | |
350 | |
351 (defn parse-midi-note-list | |
352 [midi-note-list-str] | |
353 (let [[channel note velocity] | |
354 (parse-number-list midi-note-list-str)] | |
355 {:channel channel :note note :velocity velocity})) | |
356 | |
357 | |
358 (defmethod parse-command :Note_on_c | |
359 [command] | |
360 (update-in command [:args] parse-midi-note-list)) | |
361 | |
362 (defmethod parse-command :Note_off_c | |
363 [command] | |
364 (update-in command [:args] parse-midi-note-list)) | |
365 | |
366 (defmethod parse-command :Header | |
367 [command] | |
368 (let [args (:args command) | |
369 [format num-tracks division] (parse-number-list args)] | |
370 (assoc command :args | |
371 {:format format | |
372 :num-tracks num-tracks | |
373 :division division}))) | |
374 | |
375 (defmethod parse-command :Program_c | |
376 [command] | |
377 (let [args (:args command) | |
378 [channel program-num] (parse-number-list args)] | |
379 (assoc command :args | |
380 {:channel channel | |
381 :program-num program-num}))) | |
382 | |
383 | |
384 (defn parse-midi [#^File midi-file] | |
385 (map | |
386 (comp parse-command | |
387 (fn [line] | |
388 (let [[[_ channel time command args]] | |
389 (re-seq command-line line)] | |
390 ;;(println (re-seq command-parse-1 line)) | |
391 {:channel (Integer/parseInt channel) | |
392 :time (Integer/parseInt time) | |
393 :command (keyword command) | |
394 :args (apply str (drop 2 args))}))) | |
395 (drop-last | |
396 (clojure.string/split-lines | |
397 (raw-midi-text midi-file))))) | |
398 |