[plt-scheme] OS X Core MIDI and DrScheme

From: Paul C. Fisher (pfisher at mobeard.org)
Date: Tue Feb 28 14:21:19 EST 2006

First some background:

I teach a high school CS course using HtDP. I also teach a computer music
course which has a component on algorithmic composition. Recently I
stumble upon a Scheme based tool for OS X called impromptu which provides
direct access to the Core MIDI drivers (specifically one has access to the
audio units). It is a nifty little tool but the Scheme environment is far
inferior to what is available in DrScheme and I would like to be able to
cross-fertilize between the CS course and the music course.

So here are my questions:

* Is anyone familiar with impromptu?

*How hard would it be to incorporate such capabilities into DrScheme? (or
are such things already available and I haven't looked in the correct
places?)

*Is this the correct list for asking such a question? (the HtDP list
didn't seem like the correct place)

*Are there any audio and/or music capabilities/libraries for DrScheme,
specifically something which can interface with MIDI and/or Core Audio,
beyond the Teachpacks which are already bundled?

To give a sense of how impromptu works here is a snippet of code. I would
like to be able to bring this into the realm of the design recipe approach
of HtDP and I would like to be able to bring the full power of MrEd to
this tool.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 
;; PIANO PHASE EXAMPLE 
;;
;; Define and play Steve Reich Piano Phase
;;
;; Create a play-sequence function that 
;; loops using a quasi-recursive timed
;; callback.
;;
;; Try changing some of the values in the
;; pitch list while play-sequence is looping.
;; You should hear your changes take effect
;; on the next callback loop.
;;
;; While piano phase is playing try adding 
;; the line (play-note time inst 95 80 1.0)
;; to the start of the play-sequence
;; function and re-evaluate.
;;
;; (define (play-sequence time inst inc)
;;    (play-note time inst 95 80 1.0)
;;    (map (lambda (p d r)
;;    ...
;;    ...
;;  )))
;;
;; The newly evaluated function will be 
;; automatically called by callback on the next 
;; cycle. The ability to re-define functions in
;; callback loops at runtime is a handy
;; live programming technique.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; make sure that nothing is already connected
(au-clear-graph)

; setup simple au graph
; piano -> output
(define piano (au-create-node "aumu" "dls " "appl"))
(au-connect-node piano 0 *au-output-node* 0)
(au-update-graph)
(program-change (now) piano 0)
              
; define a Cmajor scale
(define *pitches* '(64 66 71 73 74 66 64 73 71 64 74 73))
(define *pitches2* '(73 66 71 64 74 66 69 73 71 64 74 73))
(define *pitches3* '(64 66 69 73 74 66 ))
(define *pitches4* '(64 73 71 64 74 73))
; choose random dynamics
(define *dynamics* (make-list (length *pitches*) 
                              (lambda (i) (random 50 110))))
(define *rhythms* (make-list (length *pitches*) 0.18))
(define *rhythms2* (make-list (length *pitches2*) 0.18))
(define *rhythms3* (make-list (length *pitches3*) 0.18))

(define *go* #t)

; loop a note sequence incrementing 'time'
; by the previous rhythm value. Allow
; for an increment value that will slighly
; delay playback.
;
; Rhythm values (in beats) need to be multipled by
; *second* to provide a number in samples.
(define (play-sequence time inst inc)
   (map (lambda (p d r)
           (play-note time inst p d r)
           (set! time (+ time (* r *second*))))
        *pitches*
        *dynamics*
        *rhythms*)
   (if *go* 
       (callback time 'play-sequence (+ time inc) inst inc)))

; loop a note sequence incrementing 'time'
; by the previous rhythm value. Allow
; for an increment value that will slighly
; delay playback.
;
; Rhythm values (in beats) need to be multipled by
; *second* to provide a number in samples.
(define (play-sequence2 time inst inc)
   (map (lambda (p d r)
           (play-note time inst p d r)
           (set! time (+ time (* r *second*))))
        *pitches2*
        *dynamics*
        *rhythms2*)
   (if *go* 
       (callback time 'play-sequence2 (+ time inc) inst inc)))

; loop a note sequence incrementing 'time'
; by the previous rhythm value. Allow
; for an increment value that will slighly
; delay playback.
;
; Rhythm values (in beats) need to be multipled by
; *second* to provide a number in samples.
(define (play-sequence3 time inst inc)
   (map (lambda (p d r)
           (play-note time inst p d r)
           (set! time (+ time (* r *second*))))
        *pitches3*
        *dynamics*
        *rhythms3*)
   (if *go* 
       (callback time 'play-sequence3 (+ time inc) inst inc)))

(define (play-sequence4 time inst inc)
   (map (lambda (p d r)
           (play-note time inst p d r)
           (set! time (+ time (* r *second*))))
        *pitches4*
        *dynamics*
        *rhythms3*)
   (if *go* 
       (callback time 'play-sequence4 (+ time inc) inst inc)))

; start two sequence with a phase of 500 samples
(define (start time)
   (set! *go* #t)
   (play-sequence time piano 0)
   (play-sequence2 time piano 23)
   (play-sequence3 time piano 109)
   (play-sequence time piano 220)
   (play-sequence4 time piano 30)
   (play-sequence time piano 500))

(define (stop)
   (set! *go* #f))

; STOP
(stop)
   
; START
(start (now))

Paul C. Fisher
Science Department / Director of Technology
Morristown-Beard School
http://www.mobeard.org/



Posted on the users mailing list.