Mercurial > vba-clojure
comparison 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 |
comparison
equal
deleted
inserted
replaced
437:20a9d5faf47c | 438:067ea3f0d951 |
---|---|
388 (defn silence [length] | 388 (defn silence [length] |
389 {:frequency 1 | 389 {:frequency 1 |
390 :duration length | 390 :duration length |
391 :volume 0}) | 391 :volume 0}) |
392 | 392 |
393 (defn midi->mini-midi [#^File midi-file] | 393 (defn commands |
394 "return all events where #(= (:command %) command)" | |
395 [command s] | |
396 (filter #(= command (:command %)) s)) | |
397 | |
398 (defn midi-track->mini-midi [#^File midi-file track-num] | |
394 (let [midi-events (parse-midi midi-file) | 399 (let [midi-events (parse-midi midi-file) |
395 | 400 |
396 note-on-events | 401 note-on-events (commands :Note_on_c midi-events) |
397 (filter #(= :Note_on_c (:command %)) midi-events) | 402 note-off-events (commands :Note_off_c midi-events) |
398 note-off-events | 403 |
399 (filter #(= :Note_off_c (:command %)) midi-events) | 404 select-channel |
400 | 405 (fn [n s] |
401 channel-1-on | 406 (sort-by :time (filter #(= n (:channel (:args %))) s))) |
402 (sort-by :time | 407 |
403 (filter #(= 1 (:channel (:args %))) | 408 channel-on (select-channel track-num note-on-events) |
404 note-on-events)) | 409 |
405 channel-1-off | 410 channel-off (select-channel track-num note-off-events) |
406 (sort-by :time | |
407 (filter #(= 1 (:channel (:args %))) | |
408 note-off-events)) | |
409 | 411 |
410 | 412 |
411 tempo (:args (first (filter #(= :Tempo (:command %)) midi-events))) | 413 tempo (:args (first (commands :Tempo midi-events))) |
412 division (:division | 414 division |
413 (:args (first (filter #(= :Header (:command %)) midi-events)))) | 415 (:division (:args (first (commands :Header midi-events)))) |
414 | 416 |
415 notes | 417 notes |
416 (map | 418 (map |
417 (fn [note-on note-off] | 419 (fn [note-on note-off] |
418 {:frequency (midi-code->frequency (:note (:args note-on))) | 420 {:frequency (midi-code->frequency (:note (:args note-on))) |
421 (- (:time note-off) (:time note-on))) | 423 (- (:time note-off) (:time note-on))) |
422 1e6) ;; convert clock-pulses into seconds | 424 1e6) ;; convert clock-pulses into seconds |
423 :volume (int (/ (:velocity (:args note-on)) 10)) | 425 :volume (int (/ (:velocity (:args note-on)) 10)) |
424 :time-stamp (/ (* (/ tempo division) | 426 :time-stamp (/ (* (/ tempo division) |
425 (:time note-on)) 1e6)}) | 427 (:time note-on)) 1e6)}) |
426 channel-1-on channel-1-off) | 428 channel-on channel-off) |
427 | 429 |
428 silences | 430 silences |
429 (map (fn [note-1 note-2] | 431 (map (fn [note-1 note-2] |
430 (let [note-1-space (- (:time-stamp note-2) | 432 (let [note-1-space (- (:time-stamp note-2) |
431 (:time-stamp note-1)) | 433 (:time-stamp note-1)) |
435 (concat [(assoc (silence 0) | 437 (concat [(assoc (silence 0) |
436 :time-stamp 0)] notes) | 438 :time-stamp 0)] notes) |
437 notes) | 439 notes) |
438 | 440 |
439 notes-with-silence | 441 notes-with-silence |
440 (filter (comp not zero? :duration) (interleave silences notes)) | 442 (filter (comp not zero? :duration) |
441 ] | 443 (interleave silences notes))] |
442 | |
443 (map | 444 (map |
444 (fn [note-event] | 445 (fn [note-event] |
445 (note-codes (:frequency note-event) | 446 (note-codes (:frequency note-event) |
446 (:volume note-event) | 447 (:volume note-event) |
447 (int (* (:duration note-event) 0x100)))) | 448 (int (* (:duration note-event) 0x100)))) |
448 notes-with-silence))) | 449 notes-with-silence))) |
449 | 450 |
451 (defn midi->mini-midi [#^File midi-file] | |
452 {:track-1 (flatten (midi-track->mini-midi midi-file 1)) | |
453 :track-2 (flatten (midi-track->mini-midi midi-file 2))}) | |
454 | |
455 (defn play-midi [#^File midi-file] | |
456 (let [track-1-target 0xA000 | |
457 track-2-target 0xB000 | |
458 program-target 0xC000 | |
459 mini-midi (midi->mini-midi midi-file) | |
460 long-silence (flatten (note-codes 20 0 9001))] | |
461 | |
462 (-> (second (music-base)) | |
463 (set-memory-range track-1-target long-silence) | |
464 (set-memory-range track-2-target long-silence) | |
465 (set-memory-range track-1-target (:track-1 mini-midi)) | |
466 (set-memory-range track-2-target (:track-2 mini-midi)) | |
467 (set-memory-range program-target (music-kernel)) | |
468 (PC! program-target)))) | |
469 | |
470 | |
471 | |
450 | 472 |
451 (def C4 (partial note-codes 261.63)) | 473 (def C4 (partial note-codes 261.63)) |
452 (def D4 (partial note-codes 293.66)) | 474 (def D4 (partial note-codes 293.66)) |
453 (def E4 (partial note-codes 329.63)) | 475 (def E4 (partial note-codes 329.63)) |
454 (def F4 (partial note-codes 349.23)) | 476 (def F4 (partial note-codes 349.23)) |
475 program-target (music-kernel)) | 497 program-target (music-kernel)) |
476 (set-memory-range music-target music-bytes) | 498 (set-memory-range music-target music-bytes) |
477 (PC! program-target)))) | 499 (PC! program-target)))) |
478 | 500 |
479 | 501 |
502 | |
480 ;; (defn test-note [music-bytes] | 503 ;; (defn test-note [music-bytes] |
481 ;; (-> (set-memory-range (second (music-base)) | 504 ;; (-> (set-memory-range (second (music-base)) |
482 ;; 0xC000 (concat (clear-music-registers) | 505 ;; 0xC000 (concat (clear-music-registers) |
483 ;; (play-note) | 506 ;; (play-note) |
484 ;; (infinite-loop))) | 507 ;; (infinite-loop))) |