Common Music provides many high level functions and macros that allow musical material to be algorithmically described. It is usually easiest to develop this sort of code in a text editor that supports Lisp evaluation and formatting. If your editor does not understand Lisp then you can still evaluate expressions by using copy/paste to Stella's prompt and then pressing the Return key.
Stella [Top-Level]: (setf foo #!top-level) #<CONTAINER: Top-Level> Stella [Top-Level]: ,foo #<CONTAINER: Top-Level> Stella [Top-Level]:Use nth-object to reference a sub-object:
Stella [Top-Level]: (nth-object 0 foo) #<THREAD: Pulse> Stella [Top-Level]:Note that nth-object indexing starts at 0.
The following links will take you to detailed documentation for each of the macros:
(merge m () (loop for p in '(c4 c5 c6) for s from 0 for n in '(nood1 nood2 nood3) do (algorithm n midi-note (start s amplitude .1) (setf note (item (intervals 0 2 3 5 7 8 in random from p) :kill 10)) (setf rhythm (item (rhythms e s 32 in random))) (setf duration rhythm))))The composer wants to create three algorithms to execute inside a merge in which each algorithm generates a different series of notes based on the three different values of p. The code would work except for two problems. Since the algorithm name is a single symbol n, loop will create one algorithm named n three times, rather than creating three different algorithms. Each algorithm must have its own unique name or no name at all. Use name to create a new name for each new value of n. Our previous example now looks like:
(merge m () (loop for p in '(c4 c5 c6) for s from 0 for n in '(nood1 nood2 nood3) do (algorithm (name n) midi-note (start s amplitude .1) (setf note (item (intervals 0 2 3 5 7 8 in random from p) :kill 10)) (setf rhythm (item (rhythms e s 32 in random))) (setf duration rhythm))))Unfortunately, this code will still not work correctly. In order to understand why we must first look at loop and algorithm a bit more closely.
However, in the preceding example the composer means for each algorithm to use a different value of p -- for the first algorithm p should be C4, for the second C5, and for the third C6. Lexical closures capture a variable binding (definition); this is not necessarily the same as the variable's value, since a setq changes the value but not the binding. Use the macro with-vars-snapshotted to ensure that each discrete value of a loop variable is captured:
(merge m () (loop for p in '(c4 c5 c6) for s from 0 for n in '(nood1 nood2 nood3) do (with-vars-snapshotted (p s n) (algorithm (name n) midi-note (start s amplitude .1) (setf note (item (intervals 0 2 3 5 7 8 in random from p) :kill 10)) (setf rhythm (item (rhythms e s 32 in random))) (setf duration rhythm)))))The algorithms defined in the loop now work as expected.
The syntax of vars is identical to the binding list of let: each form is either the name of a variable, or a binding list (variable value), where variable is the name of the variable and value is its initial value. A vars declaration is processed in sequential order so variables can refer to the variables "to their left" in their value statement. For example,
(vars a (b 2) (c (* b 3)))declares a to be nil, b to be 2 and c to be 6.
Here is a comparison of three different ways of defining a variable x for an algorithm:
(let ((x (random 10))) (algorithm foo midi-note () (print x) ...)
(algorithm foo midi-note (length 100) (vars (x (random 10))) (print x) ...)
(algorithm foo midi-note (length 100) (let ((x (random 10)) (print x) ...)
(algorithm ritardando midi-note (amplitude .9) (vars (len (between 5 30))) (setf note (item (notes c4 d ef for len) :kill t)) (setf rhythm (interpl count 0 .1 (1- len) .3)) (setf duration rhythm)) Stella [Top-Level]: seq ritardando Start time offset: (<cr>=None) 2 Number of times to sequence: (<cr>=1) 5 Length of pause between selections: (<cr>=None) 1 Stella [Top-Level]:Here is a more complicated, but very elegant, example written by Tobias Kunze ( tkunze@ccrma.stanford.edu)
;;; A third-order recursive cellular automaton. ;;; The algorithm maintains three past note values ;;; to compute each new note based on the formula: ;;; ;;; (+ last ;;; (* (- 12 (abs x)) (if (>= x 0) -1 1)) ;;; 1) ;;; ;;; where x represents the interval from the oldest to the ;;; second-to-oldest note. Sounds best with a softly ;;; reverberated percussive sound (vibe, harp or piano). Set ;;; length to some higher number (ca. 1000 or more) to see that ;;; this generates up to 24 different patterns in lots of ;;; different phrases (algorithm cell midi-note (length 200 rhythm .1 duration .5 amplitude .5) (with-past-values ((note 3 60 60 60)) ;; convert oldest interval to inverse complement (let ((width (- (- (past-value note 3) (past-value note 2))))) (incf width (if (>= width 0) -12 12)) ;; transpose by last note. if the new note is out of ;; bounds shift it up or down and increment by whole step ;; otherwise increment by half step (incf width (past-value note 1)) (setf note (cond ((< width 36) ; raise 1 to 5 octaves+2 steps (+ width (* 12 (between 1 6)) 2)) ((> width 98) ; lower 1 to 5 octaves-2 steps (- width (* 12 (between 1 6)) -2)) (t (+ 1 width))))))) Stella [Top-Level]: mix cell 1 Stella [Top-Level]:
The next example defines a mute named Foo that prints its current count and time values as it executes:
(mute foo (length 4 rhythm .5) (format t "~%Count=~S, time=~S" count time)) Stella [Top-Level]: mix foo Start time offset: (<cr>=None) <cr> [Foo executes but no sound results] Count=0, time=0.0 Count=1, time=0.5 Count=2, time=1.0 Count=3, time=1.5 Count=4, time=2.0 Stella [Top-Level]:
To avoid sprouted objects appearing in the Top-Level container specify nil as thir name. Unnamed structure is is never included in Top-Level.
Here is a example of a mute that sprouts 6 anonymous algorithms. Each time the mute executes it binds the variable off to a new pitch and the repeat facto rep to a value between 10 and 20.
(mute ma (rhythm 2) (let ((off (item (degrees c3 c4 c5 ) :kill 2)) (rep (between 20 30))) (sprout (algorithm nil midi-note (rhythm .2 duration .175 start (+ time (random .05)) amplitude .5) (setf note (item (intervals 0 2 3 5 7 8 9 in heap from off for rep returning note) :kill t)))))) Stella [Top-Level]: open test.midi Stream: #<File: "test.midi">. Stella [Top-Level]: mix ma 0 Play file test.midi? (<cr>=Yes)
(defun sierpinski (nam dur key amp rep &optional (tim 0)) (algorithm (name nam) midi-note (start tim rhythm dur amplitude amp) (setf note (item (intervals 0 11 6 from key) :kill t)) (when (> rep 1) (sprout (sierpinski nil (/ rhythm 3) note amp (1- rep) time))))) Stella [Top-Level]: (sierpinski 'main 12 'c2 .5 5) #<Algorithm: Main> Stella [Top-Level]: mix main 0 Stella [Top-Level]:One interesting extenstion to the function might be for the algorithm to pass a list of intervals in addition to an offset, that way the pattern could change each time sierpinski is called.
Next Chapter
Previous Chapter
Table of Contents
Last Modified: 6-Mar-1998