;; The callback function schedules a

;; a function for execution at a precise moment

;; in the future.


;; The ability to precisly schedule the execution

;; of scheme functions is a critical technique in

;; Impromptu programming.


;; Callback functions take the following form

;; (callback time-to-execute function argA argB ...)


;; The callback function's first argument is a

;; time in the future to execute the function.

;; The second argument is a function or more commonly 

;; the symbol name of a function to be called

;; back. The remaining arguments are the arguments

;; of the function being called back.


;; Pass a function if you do not want re-definitions

;; of the function to effect the scheduled callback.

;; Use a symbol if you do want re-definitions of the

;; funtion to effect the scheduled callback.


;; To stop a recursive callback from continuously

;; looping re-define the function without including

;; the callback so the next time the function is

;; called it will not loop again.



; callback the print function in two seconds from now,

; pass the "Hello World" string as an argument

; to the print function.

(callback (+ (now) (* 2 *second*)) 'print "Hello World")

; setup a quasi recursive callback loop

; whereby a function continuously reschedules

; a callback to itself.

; This function will print a string to the log

; view every second.

(define my-function 

   (lambda (string)

      (print string)

      (callback (+ (now) *second*) 'my-function string)))

; call my-function to start the recursive loop

(my-function "Hello Impromptu")

; stop the loop by redefining the function without a callback

(define my-function (lambda (string) (print "stopped")))

; A less complicated way to redefine the symbol is simply

(define my-function)

; A simple musical example using callback.

; Set up the audio unit graph.


(define drums (au:make-node "aumu" "dls " "appl"))

(au:connect-node drums 0 *au:output-node* 0)


; global value to control loop callback

(define *loop-go* #t)

; The dls instrument has drums on midi channel 9

; so we need to add the channel as an extra argument

; to the play-note function.


; In order to keep the timing accurate, pass the variable

; time within the function rather than using (now) again

; and again. Otherwise a small inaccuracy would creep in

; because of the time it takes to evaluate the now function each time.

(define loop

   (lambda (time)

      (play-note time drums 36 80 11025 9)

      (play-note (+ time (/ *second* 4)) drums 38 80 11025 9)

      (if *loop-go*

          (callback (+ time (/ *second* 2)) 'loop (+ time (/ *second* 2))))))

; stop drums

(define stop

   (lambda ()

      (set! *loop-go* #f)))

; start drums

(define start

   (lambda ()

      (set! *loop-go* #t)

      (loop (now))))