;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;

;; 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))