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

;; 

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