Everyone: 1) Please include a working sound file and working lisp code on your web page. If you lisp code uses an instrument (e.g., singer.ins), your web page must link to that instrument. 2) It's much easier to start simple and grow than to write something big and then try to debug it. 3) Things will be much easier (for you!) to understand and test if you "factor" common code into subroutines. 4) LET is almost always easier to understand than SETF, because you don't need to worry about whether the variable already exists or anywhere else that it may be used. 5) SETF can be needlessly confusing in loops too: (loop with x = 0 repeat 10 do (print x) (setf x (+ x 1)) ) (loop for x from 0 below 10 do (print x)) -------------------------------------------------- sbacker: http://ccrma-www.stanford.edu/~sbacker/220b/hw2.htm Logistic function with p=440. 440 is a highly unstable value: > (run-logistics (random 1.0) 440 :times 4) (63.007267 -1719039.8 -1.3002436e+15 -7.438787e+32) This value, squared, was the "noise amount" parameter for Sheila, which was way out of bounds. bloland: http://ccrma-www.stanford.edu/~bloland/220b/hw2.htm "multichanters" cellular automata. Each cell's state (0/1) determines the volume of a syllable. Nice pick-vowel and pick-consonant procedures Main procedure ("chanter") would be easier to understand by breaking things into subprocedures. For example, I'd use a separate procedure to turn a single generation of the automata into sheila parameters. bburket: http://ccrma-www.stanford.edu/~bburkett/220b/hw2.htm Cellular automata population selects Sheila's shape and glottal source by selecting from a list of possibilities. Each note has 4-5 Sheila "frames" with other parameters predetermined (e.g., glissando frequencies.) Suggestion: instead of having multiple copies of this code: (nth (mod pop1 (length shape)) shape) there should be a helper procedure: (defun nthmod (n list) (nth (mod n (length list)) list) ) So this: (singer time2 0.05 `((1 ,(nth (mod pop2 (length shape)) shape) ,(nth (mod pop2 (length glot)) glot) 200 0.8 0 0) (1 ,(nth (mod (+ 5 pop2) (length shape)) shape) ,(nth (mod (- 3 pop2) (length glot)) glot) 150 0.6 0 0) (1 ,(nth (mod (+ 2 pop2) (length shape)) shape) ,(nth (mod (- 5 pop2) (length glot)) glot) 120 0.5 0 0) (1 ,(nth (mod (+ 7 pop2) (length shape)) shape) ,(nth (mod (- 9 pop2) (length glot)) glot) 80 0.9 0 0) (1 ,(nth (mod (+ 1 pop2) (length shape)) shape) ,(nth (mod (- 1 pop2) (length glot)) glot) 180 0.7 0 0))) would look like this: (singer time2 0.05 `((1 ,(nthmod pop2 shape) ,(nthmod pop2 glot) 200 0.8 0 0) (1 ,(nthmod (+ 5 pop2) shape) ,(nthmod (- 3 pop2) glot) 150 0.6 0 0) (1 ,(nthmod (+ 2 pop2) shape) ,(nthmod (- 5 pop2) glot) 120 0.5 0 0) (1 ,(nthmod (+ 7 pop2) shape) ,(nthmod (- 9 pop2) glot) 80 0.9 0 0) (1 ,(nthmod (+ 1 pop2) shape) ,(nthmod (- 1 pop2) glot) 180 0.7 0 0))) jcaceres: http://ccrma-www.stanford.edu/~jcaceres/220b/hw2.htm Cellular automata. Each alive cell results in a Sheila frame with random frequency, stepping through lists of glottals and vocal shapes. Noise amp is always very high (0.1), so whole thing has a nice noisy character. jcarlile: http://ccrma-www.stanford.edu/~jcarlile/220b/hw2.htm "lorenz' annoyance" Lorenz curves determine spatial position (x+y interpreted as rectangular coordinates and map to polar angle (panning) and distance (amplitude)) and frequency (z) Suggestion: Instead of (funcall 'bug time dur amp :degree pos :freq freqz) just say (bug time dur amp :degree pos :freq freqz) jconte: http://ccrma-www.stanford.edu/~jconte/220b/hw2.htm "All Work and No Play Makes Sheila Go Bonkers." neat timeline code: (with-sound (:srate 44100 :channels 2) (loop ;;; start with a non-random state for all cells with cells = (make-cells '(0 0 1 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 0 1 0)) repeat 25 for time from 0 by 1 for population = (population cells) do (singer time .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitcha cells)) .8 0.0 .00))) (singer (+ time 5.5) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitcha cells)) .8 0.0 .00))) (singer (+ time 10.25) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (+ (make-pitcha cells) 3)) .8 0.0 .00))) (singer (+ time 15.75) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitcha cells)) .8 0.0 .00))) (singer (+ time 20.125) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitchb cells)) .8 0.0 .00))) (singer (+ time 20.375) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitchb cells)) .8 0.0 .00))) (singer (+ time 22.625) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitchb cells)) .8 0.0 .00))) (singer (+ time 22.825) .1 `((.4 ,(make-shape cells) test.glt ,(hertz (make-pitchb cells)) .8 0.0 .00))) (setf cells (step-cells cells)))) How does this work? Time to show off some Lisp tricks: ;; Jack's compositional genius expressed as a list of the 8 key numbers (defparameter magic '(0 5.5 10.25 15.75 20.125 20.375 22.625 22.825)) ;; These numbers are added to the current time (defun add-magic (seconds) (mapcar #'(lambda (x) (+ seconds x)) magic) ) (add-magic 0) (add-magic 1) (add-magic 2) ;; The numbers from 0 to 24: (loop for i from 0 to 24 collect i) ;; Add the magic to each of the numbers from 0 to 24: (mapcar #'add-magic (loop for i from 0 to 24 collect i)) ;; Mush that into one big list (apply #'append (mapcar #'add-magic (loop for i from 0 to 24 collect i))) ;; Sort it so we can see when the notes occur: (sort (apply #'append (mapcar #'add-magic (loop for i from 0 to 24 collect i))) #'<) First three cells in cellular automata array determine shape and two pitches. Let's simplify the code. First version: ;;examines the first element of an array, outputs corresponding shape (defun make-shape (cells) ;;use the neighbors idea from above (let* ((neighbors (list (aref cells 0) (aref cells 1) (aref cells 2)))) ;; set various shapes based on various conditions (cond ((equal neighbors '(0 0 0)) 'ehh.shp) ((equal neighbors '(0 0 1)) 'eee.shp) ((equal neighbors '(0 1 0)) 'uhh.shp) ((equal neighbors '(0 1 1)) 'oo.shp) ((equal neighbors '(1 0 0)) 'aw.shp) ((equal neighbors '(1 0 1)) 'ahh.shp) ((equal neighbors '(1 1 0)) 'ihh.shp) ((equal neighbors '(1 1 1)) 'ttt.shp) (t nil)))) ;;outputs a pitch in midi notation based on cellular automata patterns (defun make-pitcha (cells) ;;use the neighbors idea from above (let* ((neighbors (list (aref cells 0) (aref cells 1) (aref cells 2)))) ;; set various pitches based on various conditions (cond ((equal neighbors '(0 0 0)) 60) ((equal neighbors '(0 0 1)) 60) ((equal neighbors '(0 1 0)) 64) ((equal neighbors '(0 1 1)) 64) ((equal neighbors '(1 0 0)) 64) ((equal neighbors '(1 0 1)) 67) ((equal neighbors '(1 1 0)) 67) ((equal neighbors '(1 1 1)) 67) (t nil)))) (defun make-pitchb (cells) ;;use the neighbors idea from above (let* ((neighbors (list (aref cells 0) (aref cells 1) (aref cells 2)))) ;; set various pitches based on various conditions (cond ((equal neighbors '(0 0 0)) 40) ((equal neighbors '(0 0 1)) 45) ((equal neighbors '(0 1 0)) 50) ((equal neighbors '(0 1 1)) 55) ((equal neighbors '(1 0 0)) 65) ((equal neighbors '(1 0 1)) 70) ((equal neighbors '(1 1 0)) 75) ((equal neighbors '(1 1 1)) 80) (t nil)))) See how much these have in common? Let's separate it into the part that examines the first three cells and the part that chooses something based on what they are: (defun state-of-first-three-cells (let* ((neighbors (list (aref cells 0) (aref cells 1) (aref cells 2)))) (cond ((equal neighbors '(0 0 0)) 0) ((equal neighbors '(0 0 1)) 1) ((equal neighbors '(0 1 0)) 2) ((equal neighbors '(0 1 1)) 3) ((equal neighbors '(1 0 0)) 4) ((equal neighbors '(1 0 1)) 5) ((equal neighbors '(1 1 0)) 6) ((equal neighbors '(1 1 1)) 7) (t nil)))) (defun make-shape (cells) (nth (state-of-first-three-cells cells) '(ehh.shp eee.shp uhh.shp oo.shp aw.shp ahh.shp ihh.shp ttt.shp) )) (defun make-pitcha (cells) (nth (state-of-first-three-cells cells) '(60 60 64 64 64 67 67 67) )) (defun make-pitchb (cells) (nth (state-of-first-three-cells cells) '(40 45 50 55 65 70 75 80) )) And we can get even simpler by taking advantage of binary arithmetic: (defun state-of-first-three-cells (cells) (+ (* 4 (aref cells 0)) (* 2 (aref cells 1)) (aref cells 2) )) jeccles:http://ccrma-www.stanford.edu/~jeccles/220b/hw2.htm "Out, Come Show To Them!" Singer + FM-Violin Nice sound design; good use of layering, noise, rhythm. Population of cellular automata at each generation used to transpose precomposed melodies. Here's an extremely confusing use of setf: ;;; transpose melody up based on cell population, melody size and cell repeats must be equal (defun trans (cells melody) (let ((tempmel (make-list (length melody) :initial-element 0))) (loop for i from 0 below (length melody) for population = (population cells) do (setf (nth i tempmel) (transpose (nth i melody) population)) ) (setf newmel tempmel)) ) CM> (trans (make-cells 12) '(g4 a4 f4 d3 d3)) ; Warning: This variable is undefined: NEWMEL (E5 FS5 D5 B3 B3) ;; Loop version: (defun trans (cells melody) (loop for note in melody collect (transpose note (population cells)))) ;; Lisp-head version: (defun trans (cells melody) (mapcar #'(lambda (note) (transpose note (population cells))) melody)) jrobfox: http://ccrma-www.stanford.edu/~jrobfox/220b/hw2.htm Population of automaton selects shape and frequency from a predefined list khavlak: http://ccrma-www.stanford.edu/~khavlak/220b/hw2.htm Logistics determine pitches (from C major scale) and rhythms Lots of nice programming: Defined solfege syllables, each task is a separate procedure. (with-sound (:srate 44100) (let ((time 0)) (play-list time (final-list 3.971 3.8 :times 50) ))) Suggestion: Instead of this COND, use an integer to index a list: (defun note-list (parameter &key (times 50)) (let* ((data (run-logistics (random 1.0) parameter :times times))) (loop for d in data collect (cond ((< d .125) 'do-1) ((< d .25) 're) ((< d .375) 'mi) ((< d .5) 'fa) ((< d .625) 'sol) ((< d .75) 'la) ((< d .875) 'si) ((< d 1.01) 'do-2)) ) )) (note-list 2.0 :times 10) ;; Matt's version (defun note-list (parameter &key (times 50)) (let* ((data (run-logistics (random 1.0) parameter :times times))) (loop for d in data collect (nth (floor (* d 8)) '(do-1 re mi fa sol la si do-2))) ))) paulguy: http://ccrma-www.stanford.edu/~paulguy/220b/hw2.htm "Sheilasaurus" Cellular automata (with his own rule set), population maps to frequency. rlugo: http://ccrma-www.stanford.edu/~rlugo/220b/hw2.htm Multiple cellular automata, each with population controlling a parameter Word: selects "Trogdor" or "Burninate", each a big chunk of code Frequency, Rhythm, Duration: select from list of predetermined values gautham: http://ccrma-www.stanford.edu/~gautham/220b/hw2.htm Populations of cellular automata select from predetermined pitches. Another callular automata's population determines vibrato amount. Helper procedures for syllables ruviaro: http://ccrma-www.stanford.edu/~ruviaro/220b/hw2.htm Unique and creative use of cellular automata: - There are always 5 phonemes in every call to singer. - Each cell corresponds to a procedure that advances the duration, frequency, glottal amplitude, noise amplitude, or vibrato of one particular phoneme. - On each generation, if a cell is alive, its procedure is called. Otherwise, that parameter stays the same as the previous call to singer. This is an example of a program that really should use SETF! Suggestion: Instead of (defun f1 () (let* ((x (+ freq1 (* 0.9 (- (* freq1 0.5) (random freq1)))))) (if (< x 0) (setf freq1 (* x -1)) (setf freq1 x)))) "(defun f1-matt () (setf freq1 (abs (+ freq1 (* 0.9 (- (* freq1 0.5) (random freq1))))))) mlwilcox: http://ccrma-www.stanford.edu/~mlwilcox/220b/hw2.htm Population of two cellular automata determine note lengths and vibrato, lwolfson: http://ccrma-www.stanford.edu/~lwolfson/220b/hw2.htm "Many Little Devils Learning to Swim" Population of cellular automata determines melodic interval (always alternating upwards and downwards). Nice layering of multiple singers.