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

;;

;; MUSIC PROGRAM GENERATOR

;;

;; This example demonstrates a very basic shell

;; for generating 'programs' that write music.

;; Think of the 'program' as a tree with *functions*

;; representing branch points and *terminals*

;; as leaf nodes.

;;

;; Note that nothing has been done to make these 

;; results in anyway musical.  Let your mind wander :) 

;;

;; Try adding more *terminals* and *functions*

;;

;; Not much is needed to extend this into a

;; very basic Genetic Programming system.

;; (a) Create a 'generation' of random programs

;; (b) Create a crossover function

;; (c) Create a fitness function to 'judge' melody fitness

;;

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


(au:clear-graph)


; Setup AudioGraph

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

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

(au:update-graph)


;; select random pitch from pitch class 

;; bounded by lower and upper (inclusive lower exclusive upper)

;;

;; arg 1: lower bound (inclusive) 

;; arg 2: upper bound (exclusive)

;; arg 3: pitch class

;;

;; returns -1 if no valid pitch was found 

;;

(define random-pc

   (lambda (lower upper pc)

      (if (null? pc) 

          -1

          (let loop ((val (random lower upper)) (count 0))

             (if (> count 50) 

                 -1                   

                 (if (memv (fmod val 12) pc) 

                     val

                     (loop (random lower upper) (+ count 1))))))))


; assign some terminals

; terminals are leaf nodes

(define *terminals* '(int-12 

                      c-major

                      octave

                      index

                      (random 3)

                      (random-pc 60 72 '(0 2 4 5 7 9 11))

                      ))


; assign some functions (functions will be passed 2 arguments)

(define *functions* '(+ -))


; some terminals are symbols that need to be

; turned into something. else return t

(define (terminal t)

   (cond ((equal? t 'c-major)

          (random '(0 2 4 5 7 9 11)))

         ((equal? t 'int-12)

          (random 12))

         ((equal? t 'octave)

          (random '(12 24)))

         (else t)))


; build a function randomly choosing between

; *functions* and *terminals* along the way

(define (build-program)

   (list (random *functions*)

         (if (= 0 (random 3))

             (build-program)

             (terminal (random *terminals*)))

         (if (= 0 (random 3))

             (build-program)

             (terminal (random *terminals*)))))


; build a melody from a program

(define (build-melody program)

   (do ((melody '() (cons (abs (eval program)) 

                          melody))

        (index 0 (+ index 1)))

       ((= index 15) (reverse melody))))


; play note sequence

(define (play-sequence time inst plst)

   (map (lambda (p d r)

           (play-note time inst p d r)

           (set! time (+ time r)))

        plst

        (make-list (length plst) 80)

        (make-list (length plst) (* *second* 0.15))))


; 1) generate program

; 2) generate melody by running program

; 3) print program & melody

; 4) play melody and return program

(define (run)

   (let* ((program (build-program))

          (pitches (build-melody program)))

      (print 'program-> program)

      (print 'melody-> pitches)

      (play-sequence (now) *piano* pitches)

      program))


; START

(run)