home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-10-26 | 10.4 KB | 435 lines | [TEXT/ScoM] |
-
- A Lisp Tutorial For Musicians
-
- You may well have a book on Lisp. But, its examples are unlikely
- to have anything to do with music. This tutorial shows how you might
- begin to make sense of the primitives of Lisp in musical situations.
- These primitives are the building blocks of SCOM's functions. They
- will be invaluable as you begin to design and customize your own
- functions.
-
- This is an interactive tutorial. Evaluate every expression you meet.
- Do this by placing the cursor before the first or after the last
- bracket of an expression and pressing the ENTER key. Study the
- output in the Listener window.
-
- * All SCOM's functions are created from Lisp primitives *
-
- What is a primitive? - it's a function built into the system.
-
- Example: (+)
-
- (+ 3 7)
-
- (+ 3 7 5 6)
-
- Let's look at situations where we might use arithmetic functions:
-
- (+ 7 3)
- (- 7 3)
- (* 7 3)
- (/ 7 3)
-
- ; a list of note lengths to be added together to make a zone
-
- (setq rhy '(24 24 48 48 96 48))
-
- (setq zone (apply '+ rhy))
-
- (setq zone1 (+ 24 24 48 48 96 48))
-
- (setq zone2 (* zone 4))
-
- (setq zone3 (/ (* zone 4) zone))
-
- ; a practical example
-
- (setq len '(96 48 48))
- (setq mat '(a c b g r t a s = d e))
- (setq pat '(a b c a))
-
-
- (setq zones (symbol-repeat (length pat)
- (list (/ (* (apply '+ len)
- (length mat)) (length pat))))
- )
-
- A breakdown of this expression:
-
- A core note-length value or the sum of a list of values (len) is
- multiplied by the length of the symbol output (mat); then divided
- by the length of the tonality symbol template (pat). This value is
- then repeated the number of times corresponding to the length of
- the tonality symbol list.
-
- Note the primitives: length, apply, list
-
- (length mat)
- (apply '+ len)
- (list (/ (* (apply '+ len) (length mat))(length pat))
-
- ;_________________________________________________________________________
-
- ; functions
-
-
- (defun dec (x)
- (1- x))
-
- (dec 5)
-
- (defun inc (x)
- (1+ x))
-
- (inc 5)
-
- ; shows operation of 1+ and 1- : equivalent to (- x 1) (+ x 1)
- ; it also shows how to make a function using the primitive defun.
-
- Let's square some rhythmic values:
-
- (defun sq (x)
- (* x x))
-
- (sq 5)
-
- ; this might be used to create zones from note-lengths
-
- (defun square-values (list-of-values)
- (mapcar 'sq list-of-values))
-
- (square-values rhy)
-
- ; the primitive mapcar maps sq onto each value in the rhy list.
-
- Here's another simple function using primitives mapcar and random:
-
- (defun randomize-values (list-of-values)
- (mapcar 'random list-of-values))
-
- (setq newzones (randomize-values zones))
-
- ; and another, this time using an SCOM function integer-to-symbol:
-
- (defun integers-to-symbols (list-of-values)
- (mapcar 'integer-to-symbol list-of-values))
-
- (setq mat2 (integers-to-symbols '(0 2 5 3 2 6 4)))
-
-
- ;______________________________________________________________
-
- ; list processing
-
- (setq chords '(adf deba gib cdhi))
-
- ; try out these expressions sequentially
-
- (car chords)
- (first chords)
- (cdr chords)
- (cadr chords)
- (second chords)
- (caddr chords)
- (third chords)
- (cddr chords)
- (cdddr chords)
- (fourth chords)
- (but-last chords)
- (last chords)
-
- ; Lisp primitives car, cdr and associatives / derivatives
-
- (setq progression
- (list
- (car chords)
- (cdr chords)
- (cadr chords)
- (cddr chords)
- (cdddr chords)
- (but-last chords)
- (last chords)))
-
- ; this shows how the primitive list is used
-
- (defun select-one (pattern)
- (nth (random (length pattern)) pattern))
-
- (select-one '(1 2 3 4 5))
-
- ; a function to select one item from a list
- ; note the primitives: nth, random, length.
-
- (nth 3 '(1 2 3 4))
- (random 34)
- (length '(1 2 3 4))
-
- (select-one progression) ; evaluate this several times
-
- (setq template '(a = a = = a = a))
-
- (setq seq1 (fill-rest template (select-one progression))
- seq2 (fill-rest template (select-one progression))
- seq3 (fill-rest template (select-one progression))
- seq4 (fill-rest template (select-one progression))
- )
-
- (setq phrase (append seq1 seq2 seq3 seq4))
-
- ; the primitive append joins all the sequences together
-
- (setq link-material (fill-template template (fourth chords)))
-
- (setq phrase+link (cons phrase (list link-material)))
-
- Use cons and list to create nested expressions in the zone
- support function
-
- ; ________________________________________________________________
-
- ; predicates
-
- (cond ((and seq1 seq2) (fourth chords))
- ((and seq2 seq3) (third chords))
- ((and seq3 seq4) (second chords)))
-
- ; cond evaluates each predicate in turn until it finds one that is 'true'
-
- ; The neural expert makes extensive use of predicates
- ; this example converts symbols to note lengths
-
- (setq mat1 (gen-random 0.5 32 '(a c d a g l c c g = n m g m e d c)))
-
- (def-neuron rhyv
- (in 1 'a) '192
- (in 1 'g) '96
- (in 1 'm) '48
- (otherwise '24))
-
- (setq rhyb (run-neuron 'rhyv mat1))
-
- ; to make our own symbol to length converter just with predicates
-
- (defun symbol-to-length (symbol)
- (cond ((equal symbol 'a) '(192))
- ((equal symbol 'g) '(96))
- ((equal symbol 'm) '(48))
- (t '(24))))
-
- (symbol-to-length 'a)
-
- ; but this doesn't cope with reading a list of symbols
- ; here are two solutions
-
- (setq rhyy (flatten (mapcar 'symbol-to-length mat1)))
-
- (defun make-length (pattern)
- (prog (out)
- loop
- (cond ((null pattern) (return out)))
- (setq out
- (append out
- (symbol-to-length
- (car pattern))))
- (setq pattern (cdr pattern))
- (go loop)))
-
- (setq rhyx (make-length mat1))
-
- ;________________________________________________________________________
-
- ; recursion
-
- The function make-length shows recursion in action.
-
- Here is a full example to explain what's happening.
-
- The function retro is designed to reverse the order of a list.
-
- (defun ; function
- retro ; name
- (pattern) ;list of arguments
- (if (null pattern) nil ; a list of action the function is to take.
- (append (last pattern)
- (retro (butlast pattern)))))
-
- (retro '(48 50 52))
-
-
- 1. The if function calls (null pattern) - is this list empty?
- ( - (null pattern) is a predicate!)
-
- 2. The list is'nt empty - so nil (the then or consequent) is NOT
- returned
-
- 3. The else or alternative function append then takes the last of
- the pattern and outputs this value (52)
-
- 4. Recursion now begins! retro's argument pattern is shortened by
- butlast function to become (48 50).
-
- 5. This goes around again to if - it's still not an empty list so
- on to append and the last of (48 50) which is (50). This joins (52)
- as (52 50) - thanks to append which holds onto the values until the
- first evaluation of if is true.
-
- 6. Recursion continues via butlast bringing it down to (48)
-
- 7. This goes around again to if - still a list, even though it's one
- atom and on to append. The function last can only return (48). This
- value is then appended to the growing list (52 50 48).
-
- 8. Finally, recursion finishes as the list disappears!
-
-
- ; theme
-
- (defun retro (pattern)
- (if (null pattern) nil
- (append (last pattern)
- (retro (butlast pattern)))))
-
- ; a variation!
-
- (defun retro1 (pattern)
- (cond
- ((null pattern) nil)
- (t (append (retro1 (cdr pattern))
- (list (car pattern))))))
-
- (retro1 '(45 47 49))
-
- ; here are two extensions of the earlier select-one function
- ; the first avoids recursion.
-
- (setq mat3 '(a b c d))
-
- (defun select-two (pattern)
- (list
- (nth (random (length pattern)) pattern)
- (nth (random (length pattern)) pattern)))
-
- (select-two mat3)
-
- ; this is, in effect, similar to gen-random, and is recursive.
-
- (defun select-many (n pattern)
- (prog (out)
- loop
- (cond ((equal n (length out)) (return out)))
- (setq out (append out (list (nth (random
- (length pattern)) pattern))))
- (go loop)))
-
- (select-many 32 mat3)
-
- ; notice the predicate argument:
-
- (cond ((equal n (length out)) (return out)))
-
- This means - when the number chosen (n) becomes equal to the
- number of recursions required to make the length of out the same
- as n, out is returned.
-
- Notice how the variable out is made.
-
- (setq out (append out (list (nth etc. . .
-
- - append collects together all the different results of looping the
- expression until the predicate says 'stop!, you've reached the
- same number of recursions as the number you set'.
-
- - list has to be used because each result is a single atom - a or 1 -
- and needs to be (a) or (1) to be collected and joined together
- by append.
-
- ;__________________________________________________________________
-
- Now, for some music. The following composition example uses many of
- the functions and some of the material created in the tutorial.
- The music is a piece for keyboard and percussion. Try compiling
- the keyboard parts several times. You'll find that because of the
- select-one function the progression changes each time.
-
- Don't press the Eval Buffer button, just outline the whole example
- and Eval Selection.
-
- ; composition example - 'chordal groove'
-
- (setq tonal1 (activate-tonality (dorian c 5) (mixolydian a& 4)))
- (setq tonal2 (activate-tonality (dorian c 3) (mixolydian a& 2)))
-
- (setq chords '(adf deba gib cdhi))
-
- (setq progression
- (list
- (car chords)
- (cdr chords)
- (cadr chords)
- (cddr chords)
- (cdddr chords)
- (but-last chords)
- (last chords)))
-
- (setq template '(a = a = = a = a))
-
- (setq seq1 (fill-rest template (select-one progression))
- seq2 (fill-rest template (select-one progression))
- seq3 (fill-rest template (select-one progression))
- seq4 (fill-rest template (select-one progression)))
-
- (setq phrase (append seq1 seq2 seq3 seq4))
-
- (setq link-material (fill-template template (fourth chords)))
- (setq phrase+link (append phrase link-material))
-
- (setq chordal (append phrase phrase+link phrase))
-
- (setq mat3 '(a b c d))
- (setq mat4 (select-many 32 mat3))
-
- (setq zonea (list (* (length phrase) 48)))
- (setq zoneb (list (* (length phrase+link) 48)))
- (setq zonec (append zonea zoneb zonea))
-
- (def-instrument-symbol
- pianoRH chordal
- pianoLH (fill-template chordal mat4)
- )
-
- (def-instrument-length
- default 1/8
- )
-
- (def-instrument-rhythm
-
- cabasa '1/8 "---- ---" (z)
- bassdr '1/4 "- - -" (b)
- )
-
- (def-instrument-velocity
- default (select-many 32 '(112 84 74 64 74 54 54))
- cabasa '(96 90 84 104 0 64 74 84)
- bassdr '(110 0 0 0 84 0 0 96)
- )
-
- (def-instrument-zone
- default zonec
- )
-
- (def-instrument-tonality
- pianoRH tonal1
- pianoLH tonal2
- cabasa mt-32
- bassdr mt-32
- )
-
- (compile-instrument "ccl;output:" "groove"
- pianoRH
- pianoLH
- bassdr
- cabasa
- )
-
-
-