;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; PIANO PHASE EXAMPLE
;;
;; Define and play a simple version of Steve Reich's "Piano Phase"
;;
;; Create a play-sequence function that
;; loops using a quasi-recursive timed
;; callback, as shown in the "callbacks" tutorial.
;;
;; 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 44100)
;; to the start of the play-sequence
;; function and re-evaluate.
;;
;; (define (play-sequence time inst inc)
;; (play-note time inst 95 80 44100)
;; (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:make-node "aumu" "dls " "appl"))
(au:connect-node piano 0 *au:output-node* 0)
(au:update-graph)
; define a C major scale
(define *pitches* '(64 66 71 73 74 66 64 73 71 66 74 73))
; choose random dynamics
; The make-list function creates a list of n values
; generated by the function passed to it.
; The random function in this case picks integer values
; between the lowest and one less than the highest argument (50 - 109).
(define *dynamics* (make-list-with-proc (length *pitches*)
(lambda (i) (random 50 110))))
(define *rhythms* (make-list (length *pitches*) (* *au:samplerate* 0.18)))
; Use a flag to start and stop the loop, rather than
; the more clumsy redefinition used in previous examples.
(define *go* #t)
; Loop a note sequence incrementing 'time'
; by the previous rhythm value. Allow
; for an increment value that will slightly
; delay playback.
;
; Rhythm values (in beats) need to be multipled by
; *second* to provide a scheduling time number in samples.
; The "map' function applies the function passed to it
; to every value in the list(s) passed as argument(s).
; In this case it will execute play-note with each subsequent
; pitch, dynamic and rhythm value is the lists. Thus scheduling
; the entire phrase for playback in one parse.
; The "set!" function will update the value of a varible, in
; this case "time".
(define play-sequence
(lambda (time inst inc)
(map (lambda (p d r)
(play-note time inst p d r)
(set! time (+ time r)))
*pitches*
*dynamics*
*rhythms*)
(if *go*
(callback time 'play-sequence (+ time inc) inst inc))))
; Start two sequence with a phase of 500 samples;
; i.e., the second one will be anticipated by 500 samples
; on each loop, thus getting further and further ahead.
; Note: this process is not exactly as indicated in
; Reich's piece but the sonic effect is similar enough.
(define start
(lambda (time)
(set! *go* #t)
(play-sequence time piano 0)
(play-sequence time piano 500)))
(define stop
(lambda ()
(set! *go* #f)))
; STOP
(stop)
; START
(start (now))