Mercurial > vba-clojure
comparison clojure/com/aurellem/run/music.clj @ 484:f6b5a1914efa
merge.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 06 May 2012 20:52:31 -0500 |
parents | 221b3fea9221 |
children | 346b91ae503a |
comparison
equal
deleted
inserted
replaced
483:1bc26d1826e5 | 484:f6b5a1914efa |
---|---|
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 (def third-kind | 11 (def pony |
12 (File. "/home/r/proj/midi/third-kind.mid")) | 12 (File. user-home "/proj/vba-clojure/music/pony-title.mid")) |
13 | |
14 (def pony-csv | |
15 (File. user-home "proj/vba-clojure/music/pony-title.csv")) | |
16 | |
17 (def sync-test | |
18 (File. user-home "proj/vba-clojure/music/sync-test.mid")) | |
19 | |
20 (def drum-test | |
21 (File. user-home "proj/vba-clojure/music/drum-test.mid")) | |
22 | |
23 (def regret | |
24 (File. user-home "proj/vba-clojure/music/ship-of-regret-and-sleep.mid")) | |
25 | |
26 (def regret-csv | |
27 (File. user-home "proj/vba-clojure/music/ship-of-regret-and-sleep.csv")) | |
28 | |
29 (def mother | |
30 (File. user-home "proj/vba-clojure/music/mother.mid")) | |
31 | |
32 (def mother-csv | |
33 (File. user-home "proj/vba-clojure/music/mother.csv")) | |
34 | |
13 | 35 |
14 (defn raw-midi-text [#^File midi-file] | 36 (defn raw-midi-text [#^File midi-file] |
15 (:out | 37 (let [extention (apply str (take-last 3 (.getCanonicalPath |
16 (clojure.java.shell/sh | 38 midi-file)))] |
17 "midicsv" | 39 (cond (= "mid" extention) |
18 (.getCanonicalPath midi-file) | 40 (:out |
19 "-"))) | 41 (clojure.java.shell/sh |
42 "midicsv" | |
43 (.getCanonicalPath midi-file) | |
44 "-")) | |
45 (= "csv" extention) | |
46 (slurp midi-file)))) | |
20 | 47 |
21 (def command-line #"^(\d+), (\d+), ([^,]+)(.*)$") | 48 (def command-line #"^(\d+), (\d+), ([^,]+)(.*)$") |
22 | 49 |
23 (defmulti parse-command :command) | 50 (defmulti parse-command :command) |
24 | 51 |
168 | 195 |
169 (defn do-message | 196 (defn do-message |
170 "Read the message which starts at the current value of HL and do | 197 "Read the message which starts at the current value of HL and do |
171 what it says. Duration is left in A, and HL is advanced | 198 what it says. Duration is left in A, and HL is advanced |
172 appropraitely." | 199 appropraitely." |
173 ([] (do-message 0x16)) | 200 ([] (do-message 0x16 1)) |
174 ([sound-base-address] | 201 ([sound-base-address wave-duty] |
202 (assert (<= 0 wave-duty 3)) | |
175 (let [switch | 203 (let [switch |
176 [0x2A ;; load message code into A, increment HL | 204 [0x2A ;; load message code into A, increment HL |
177 | 205 |
178 ;; switch on message | 206 ;; switch on message |
179 0xFE | 207 0xFE |
181 | 209 |
182 0x20 | 210 0x20 |
183 :note-length] | 211 :note-length] |
184 | 212 |
185 play-note | 213 play-note |
186 [0x2A ;; load volume/frequency-high info | 214 [0x3E ;; set wave-duty |
215 (bit-shift-left wave-duty 6) | |
216 0xE0 | |
217 sound-base-address | |
218 0x2A ;; load volume/frequency-high info | |
187 0xF5 ;; push A | 219 0xF5 ;; push A |
188 0xE6 | 220 0xE6 |
189 (Integer/parseInt "11110000" 2) ;; volume mask | 221 (Integer/parseInt "11110000" 2) ;; volume mask |
190 0xE0 | 222 0xE0 |
191 (inc sound-base-address) ;;0x17 ;; set volume | 223 (inc sound-base-address) ;;0x17 ;; set volume |
201 0x2A]] ;; load duration | 233 0x2A]] ;; load duration |
202 (replace | 234 (replace |
203 {:note-length (count play-note)} | 235 {:note-length (count play-note)} |
204 (concat switch play-note))))) | 236 (concat switch play-note))))) |
205 | 237 |
238 (defn play-noise | |
239 "read [noise-code, volume, duration] and play the noise. Duration is left in | |
240 A, and HL is advanced appropraitely." | |
241 ([] | |
242 [0x2A ;; load noise-code into A | |
243 0xE0 | |
244 0x22 ;; write noise-code | |
245 | |
246 0x2A ;; load volume | |
247 0xE0 | |
248 0x21 ;; write volume | |
249 | |
250 0x2A] ;; load duration into A | |
251 )) | |
252 | |
253 | |
206 ;; (defn play-note | 254 ;; (defn play-note |
207 ;; "Play the note referenced by HL in the appropiate channel. | 255 ;; "Play the note referenced by HL in the appropiate channel. |
208 ;; Leaves desired-duration in A." | 256 ;; Leaves desired-duration in A." |
209 | 257 |
210 ;; [0x2A ;; load volume/frequency-high info | 258 ;; [0x2A ;; load volume/frequency-high info |
224 ;; 0x18 ;; set frequency-low-bits | 272 ;; 0x18 ;; set frequency-low-bits |
225 | 273 |
226 ;; 0x2A ;; load duration | 274 ;; 0x2A ;; load duration |
227 ;; ]) | 275 ;; ]) |
228 | 276 |
229 (defn music-step [sound-base-address] | 277 (defn music-step [sound-base-address wave-duty noise?] |
230 ;; C == current-ticks | 278 ;; C == current-ticks |
231 ;; A == desired-ticks | 279 ;; A == desired-ticks |
232 | 280 |
233 (flatten | 281 (flatten |
234 [;; restore variables from stack | 282 [;; restore variables from stack |
252 0xB9 ;; compare with current ticks | 300 0xB9 ;; compare with current ticks |
253 | 301 |
254 ;; if desired-ticks = current ticks | 302 ;; if desired-ticks = current ticks |
255 ;; go to next note ; set current set ticks to 0. | 303 ;; go to next note ; set current set ticks to 0. |
256 | 304 |
257 0x20 | 305 (if noise? |
258 (+ (count (do-message 0)) 2) | 306 [0x20 |
259 | 307 (+ 2 (count (play-noise))) |
260 (do-message sound-base-address) | 308 (play-noise)] |
309 | |
310 [0x20 | |
311 (+ (count (do-message 0 0)) 2) | |
312 (do-message sound-base-address wave-duty)]) | |
261 | 313 |
262 0x0E | 314 0x0E |
263 0x00 ;; 0->C (current-ticks) | 315 0x00 ;; 0->C (current-ticks) |
264 | 316 |
265 ;; save variables to stack | 317 ;; save variables to stack |
271 ])) | 323 ])) |
272 | 324 |
273 (def music-1 0x11) | 325 (def music-1 0x11) |
274 (def music-2 0x16) | 326 (def music-2 0x16) |
275 | 327 |
276 (defn music-kernel [] | 328 (defn music-kernel [wave-duty-1 wave-duty-2] |
277 (flatten | 329 (flatten |
278 [;; global initilization section | 330 [;; global initilization section |
279 (clear-music-registers) | 331 (clear-music-registers) |
280 | 332 |
281 0x3E | 333 0x3E |
311 0xF5 ;; push AF | 363 0xF5 ;; push AF |
312 0xC5 ;; push CB | 364 0xC5 ;; push CB |
313 0xE5 ;; push HL | 365 0xE5 ;; push HL |
314 | 366 |
315 | 367 |
368 ;; initialize frame 3 (noise) | |
369 0x21 | |
370 0x00 | |
371 0xA9 ;; 0xA9OO -> HL | |
372 | |
373 0xF5 ;; push AF | |
374 0xC5 ;; push CB | |
375 0xE5 ;; push HL | |
376 | |
316 ;; main music loop | 377 ;; main music loop |
317 | 378 |
318 0xE8 ;; SP + 6; activate frame 1 | 379 0xE8 ;; SP + 12; activate frame 1 |
319 6 | 380 12 |
320 (music-step music-1) | 381 (music-step music-1 wave-duty-1 false) |
321 ;;(repeat (count (music-step music-1)) 0x00) | |
322 | 382 |
323 0xE8 ;; SP - 6; activate frame 2 | 383 0xE8 ;; SP - 6; activate frame 2 |
324 (->signed-8-bit -6) | 384 (->signed-8-bit -6) |
325 ;;(repeat (count (music-step music-2)) 0x00) | 385 (music-step music-2 wave-duty-2 false) |
326 (music-step music-2) | 386 |
327 | 387 0xE8 ;; SP - 6; activate frame 3 |
388 (->signed-8-bit -6) | |
389 (music-step nil nil true) | |
328 | 390 |
329 0x18 | 391 0x18 |
330 (->signed-8-bit (+ | 392 (->signed-8-bit (+ |
331 ;; two music-steps | 393 ;; two music-steps |
332 (- (* 2 (count (music-step 0)))) | 394 (- (* 2 (count (music-step 0 0 false)))) |
395 (- (count (music-step nil nil true))) | |
333 -2 ;; this jump instruction | 396 -2 ;; this jump instruction |
334 -2 ;; activate frame 1 | 397 -2 ;; activate frame 1 |
335 -2 ;; activate frame 2 | 398 -2 ;; activate frame 2 |
399 -2 ;; activate frame 3 | |
336 ))])) | 400 ))])) |
337 | 401 |
338 (defn frequency-code->frequency | 402 (defn frequency-code->frequency |
339 [code] | 403 [code] |
340 (assert (<= 0 code 2047)) | 404 (assert (<= 0 code 2047)) |
393 (defn commands | 457 (defn commands |
394 "return all events where #(= (:command %) command)" | 458 "return all events where #(= (:command %) command)" |
395 [command s] | 459 [command s] |
396 (filter #(= command (:command %)) s)) | 460 (filter #(= command (:command %)) s)) |
397 | 461 |
398 (defn midi-track->mini-midi [#^File midi-file track-num] | 462 (defn track-info [#^File midi-file] |
399 (let [midi-events (parse-midi midi-file) | 463 (let [events (parse-midi midi-file) |
464 track-titles (commands :Title_t events) | |
465 track-info | |
466 (map #(read-string (read-string (:args %))) track-titles) | |
467 track-map | |
468 (zipmap track-info track-titles)] | |
469 track-map)) | |
470 | |
471 (defn target-tracks | |
472 "return the track-numbers in the form [voice-0 voice-1 noise]" | |
473 [#^File midi-file] | |
474 (let [track-data (track-info midi-file) | |
475 track-order | |
476 (zipmap (map :out (keys track-data)) | |
477 (vals track-data)) | |
478 channel-nums (map (comp :channel track-order) (range 3))] | |
479 channel-nums)) | |
480 | |
481 (defn midi-track->abstract-mini-midi | |
482 [#^File midi-file track-num] | |
483 (let [midi-events (parse-midi midi-file) | |
400 | 484 |
401 note-on-events (commands :Note_on_c midi-events) | 485 note-on-events (commands :Note_on_c midi-events) |
402 note-off-events (commands :Note_off_c midi-events) | 486 note-off-events (commands :Note_off_c midi-events) |
403 | 487 |
404 select-channel | 488 select-channel |
405 (fn [n s] | 489 (fn [n s] |
406 (sort-by :time (filter #(= n (:channel (:args %))) s))) | 490 (sort-by :time (filter #(= n (:channel %)) s))) |
407 | 491 |
408 channel-on (select-channel track-num note-on-events) | 492 channel-on (select-channel track-num note-on-events) |
409 | 493 |
410 channel-off (select-channel track-num note-off-events) | 494 channel-off (select-channel track-num note-off-events) |
411 | 495 |
416 | 500 |
417 notes | 501 notes |
418 (map | 502 (map |
419 (fn [note-on note-off] | 503 (fn [note-on note-off] |
420 {:frequency (midi-code->frequency (:note (:args note-on))) | 504 {:frequency (midi-code->frequency (:note (:args note-on))) |
505 :midi-code (:note (:args note-on)) | |
421 :duration | 506 :duration |
422 (/ (* (/ tempo division) | 507 (/ (* (/ tempo division) |
423 (- (:time note-off) (:time note-on))) | 508 (- (:time note-off) (:time note-on))) |
424 1e6) ;; convert clock-pulses into seconds | 509 1e6) ;; convert clock-pulses into seconds |
425 :volume (int (/ (:velocity (:args note-on)) 10)) | 510 :volume (int (/ (:velocity (:args note-on)) 10)) |
437 (concat [(assoc (silence 0) | 522 (concat [(assoc (silence 0) |
438 :time-stamp 0)] notes) | 523 :time-stamp 0)] notes) |
439 notes) | 524 notes) |
440 | 525 |
441 notes-with-silence | 526 notes-with-silence |
442 (filter (comp not zero? :duration) | 527 (concat |
443 (interleave silences notes))] | 528 (filter (comp not zero? :duration) |
529 (interleave silences notes)) | |
530 [(silence 3)])] | |
531 notes-with-silence)) | |
532 | |
533 (defn midi-track->mini-midi-voice [#^File midi-file track-num] | |
534 (let [abstract-mini-midi | |
535 (midi-track->abstract-mini-midi midi-file track-num)] | |
536 (map | |
537 (fn [note-event] | |
538 (note-codes (:frequency note-event) | |
539 (:volume note-event) | |
540 (int (* (:duration note-event) 0x100)))) | |
541 abstract-mini-midi))) | |
542 | |
543 (def midi-code->gb-noise-code | |
544 {nil 0xFF | |
545 35 87 | |
546 38 20 | |
547 39 0 | |
548 }) | |
549 | |
550 (defn noise-codes [code volume duration] | |
551 (assert (<= 0 volume 0xF)) | |
552 (if (<= duration 0xFF) | |
553 [(midi-code->gb-noise-code code code) | |
554 (bit-shift-left volume 4) | |
555 duration] | |
556 (vec | |
557 (flatten | |
558 [(noise-codes code volume 0xFF) | |
559 (noise-codes code volume (- duration 0xFF))])))) | |
560 | |
561 (defn midi-track->mini-midi-noise [#^File midi-file track-num] | |
562 (let [abstract-mini-midi | |
563 (midi-track->abstract-mini-midi midi-file track-num)] | |
444 (map | 564 (map |
445 (fn [note-event] | 565 (fn [noise-event] |
446 (note-codes (:frequency note-event) | 566 (noise-codes (:midi-code noise-event) |
447 (:volume note-event) | 567 (:volume noise-event) |
448 (int (* (:duration note-event) 0x100)))) | 568 (int (* (:duration noise-event) 0x100)))) |
449 notes-with-silence))) | 569 abstract-mini-midi))) |
570 | |
450 | 571 |
451 (defn midi->mini-midi [#^File midi-file] | 572 (defn midi->mini-midi [#^File midi-file] |
452 {:track-1 (flatten (midi-track->mini-midi midi-file 1)) | 573 (let [targets (target-tracks midi-file) |
453 :track-2 (flatten (midi-track->mini-midi midi-file 2))}) | 574 duty-info (keys (track-info midi-file))] |
575 | |
576 {:voice-1 (midi-track->mini-midi-voice midi-file (nth targets 0)) | |
577 :voice-2 (midi-track->mini-midi-voice midi-file (nth targets 1)) | |
578 :noise (midi-track->mini-midi-noise midi-file (nth targets 2)) | |
579 :duty (zipmap (map :out duty-info) | |
580 (map #(get % :duty 0) duty-info))})) | |
454 | 581 |
455 (defn play-midi [#^File midi-file] | 582 (defn play-midi [#^File midi-file] |
456 (let [track-1-target 0xA000 | 583 (let [voice-1-target 0xA000 |
457 track-2-target 0xB000 | 584 voice-2-target 0xB000 |
585 noise-target 0xA900 | |
458 program-target 0xC000 | 586 program-target 0xC000 |
459 mini-midi (midi->mini-midi midi-file) | 587 mini-midi (midi->mini-midi midi-file) |
460 long-silence (flatten (note-codes 20 0 9001))] | 588 long-silence (flatten (note-codes 20 0 20001)) |
589 long-noise-silence | |
590 (interleave (range 500) (repeat 0x00) (repeat 255)) | |
591 | |
592 voice-1 (flatten (:voice-1 mini-midi)) | |
593 wave-duty-1 ((:duty mini-midi) 0 0) | |
594 | |
595 voice-2 (flatten (:voice-2 mini-midi)) | |
596 wave-duty-2 ((:duty mini-midi) 1 0) | |
597 | |
598 noise (flatten (:noise mini-midi)) | |
599 ] | |
461 | 600 |
462 (-> (second (music-base)) | 601 (-> (second (music-base)) |
463 (set-memory-range track-1-target long-silence) | 602 (set-memory-range voice-1-target long-silence) |
464 (set-memory-range track-2-target long-silence) | 603 (set-memory-range voice-2-target long-silence) |
465 (set-memory-range track-1-target (:track-1 mini-midi)) | 604 (set-memory-range noise-target long-noise-silence) |
466 (set-memory-range track-2-target (:track-2 mini-midi)) | 605 (set-memory-range voice-1-target voice-1) |
467 (set-memory-range program-target (music-kernel)) | 606 (set-memory-range voice-2-target voice-2) |
607 (set-memory-range noise-target noise) | |
608 (set-memory-range | |
609 program-target | |
610 (music-kernel wave-duty-1 wave-duty-2)) | |
468 (PC! program-target)))) | 611 (PC! program-target)))) |
469 | 612 |
470 | 613 (defn test-noise [] |
471 | 614 (let [noise-pattern |
615 (concat (interleave (range 0x100) (repeat 0xF0) (repeat 255)) | |
616 (interleave (range 10) (repeat 0x00) (repeat 255)))] | |
617 | |
618 (-> (second (music-base)) | |
619 (set-memory-range 0xA900 (flatten noise-pattern)) | |
620 (set-memory-range 0xC000 (music-kernel 0 0)) | |
621 (PC! 0xC000)))) | |
622 | |
623 (defn test-play-noise [noise-code] | |
624 (Thread/sleep 300) | |
625 (println "playing noise:" noise-code) | |
626 (run-moves | |
627 (let [noise-pattern | |
628 (interleave (repeat 10 noise-code) (repeat 0xF0) (repeat 255))] | |
629 (-> (second (music-base)) | |
630 (set-memory-range 0xA900 (flatten noise-pattern)) | |
631 (set-memory-range 0xC000 (music-kernel 0 0)) | |
632 (PC! 0xC000))) | |
633 (repeat 20 []))) | |
634 | |
635 (defn test-all-noises [] | |
636 (dorun (map test-play-noise (range 0x100)))) | |
472 | 637 |
473 (def C4 (partial note-codes 261.63)) | 638 (def C4 (partial note-codes 261.63)) |
474 (def D4 (partial note-codes 293.66)) | 639 (def D4 (partial note-codes 293.66)) |
475 (def E4 (partial note-codes 329.63)) | 640 (def E4 (partial note-codes 329.63)) |
476 (def F4 (partial note-codes 349.23)) | 641 (def F4 (partial note-codes 349.23)) |
496 (-> (set-memory-range (second (music-base)) | 661 (-> (set-memory-range (second (music-base)) |
497 program-target (music-kernel)) | 662 program-target (music-kernel)) |
498 (set-memory-range music-target music-bytes) | 663 (set-memory-range music-target music-bytes) |
499 (PC! program-target)))) | 664 (PC! program-target)))) |
500 | 665 |
501 | |
502 | |
503 ;; (defn test-note [music-bytes] | |
504 ;; (-> (set-memory-range (second (music-base)) | |
505 ;; 0xC000 (concat (clear-music-registers) | |
506 ;; (play-note) | |
507 ;; (infinite-loop))) | |
508 ;; (set-memory-range 0xD000 music-bytes) | |
509 ;; (PC! 0xC000) | |
510 ;; (HL! 0xD000) | |
511 ;; )) | |
512 | |
513 | |
514 (defn run-program | 666 (defn run-program |
515 ([program] | 667 ([program] |
516 (let [target 0xC000] | 668 (let [target 0xC000] |
517 (-> (set-memory-range (second (music-base)) | 669 (-> (set-memory-range (second (music-base)) |
518 target program) | 670 target program) |
533 (repeat | 685 (repeat |
534 500 | 686 500 |
535 [0xF0 | 687 [0xF0 |
536 0x05])])) | 688 0x05])])) |
537 | 689 |
538 | 690 (defn play-pony [] |
691 (println "playing" (.getName pony-csv)) | |
692 (run-moves (play-midi pony-csv) (repeat 1800 []))) | |
693 | |
694 (defn play-regret [] | |
695 (println "playing" (.getName regret-csv)) | |
696 (run-moves (play-midi regret-csv) (repeat 3380 []))) | |
697 | |
698 (defn play-mother [] | |
699 (println "playing" (.getName mother-csv)) | |
700 (run-moves (play-midi mother-csv) (repeat 2200 []))) | |
701 | |
702 (defn demo [] (play-mother) (play-regret) (play-pony)) | |
703 | |
704 | |
705 |