slippery chicken documentation - "nouveau reich" &emdash; User-defined algorithms to generate and manipulate slippery-chicken data

## "Nouveau Reich" — User-defined algorithms to generate and manipulate slippery-chicken data

### + Associated files

- The slippery-chicken file from this guide
- The MIDI output generated by this file
- The PDF score produced with this file's LilyPond output
- The CMN EPS output generated by this file
- An MP3 of the piece using instrument samples

close

This demo piece, Nouveau Reich, and its accompanying tutorial, will explore the use of user-defined algorithms for the generation of slippery chicken data. The code for this piece includes three functions which create lists that are then used as the values for keyword arguments to the `make-slippery-chicken` function.

The resulting composition is an 18-minute work in the style of Steve Reich, with the content being an amalgamation of the chords from his Music for 18 Musicians and the melodic contours and phasing techniques of his Piano Phase, using slippery chicken to implement variations on his principle of gradual process in 75 lines of code.

### + The code

The code is first presented on its own here, then explained point by point below.

NB: It is strongly recommended that the user not copy and paste code from the web browser into the Lisp listener, as this can occasionally lead to errors. The code below can be downloaded in complete form under the Associated files section above.

    (defun move-first-to-end (list)
      (let (i)
        (setf i (pop list))
        (setf list (append list (list i)))))

    (defun collect-n-rotations (num-rotations list-to-rotate)
      (loop repeat num-rotations
         collect list-to-rotate
         do (setf list-to-rotate (move-first-to-end list-to-rotate))))

    (defun auto-curve-from-indices-and-items (indices items)
      (when (or (<= (apply #'min indices) 0)
                (> (apply #'max indices) (length items)))
        (error "all indices must be >0 and <= length of items"))
      (loop for pos in indices
         for x from 1
         collect x
         collect (nth (1- pos) items)))

    (let* ((num-bars 646)
           (set-pal '((1 ((fs2 b2 d4 a4 d5 e5 a5 d6)))
                      (2 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6)))
                      (3 ((cs3 fs3 e4 a4 e5 a5 e6)))
                      (4 ((fs2 cs3 e4 a4 b4 e5 a5 b5 e6)))
                      (5 ((d2 a2 e4 fs4 gs4 b4 e5 b5)))
                      (6 ((a2 e3 e4 fs4 gs4 b4 cs5 e5 b5)))
                      (7 ((cs3 fs3 fs4 gs4 a4 cs5 a5 cs6)))
                      (8 ((fs2 cs3 fs4 gs4 a4 b4 cs5 fs5)))
                      (9 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6)))
                      (10 ((d2 a2 fs4 gs4 a4 e5 a5 e6)))
                      (11 ((a2 d2 e4 fs4 a4 e5 a5)))))
           (fib-trans-ids (fibonacci-transitions
                           num-bars
                           (loop for i from 1 to (length set-pal) collect i)))
           (set-limits-lists
            (loop for pchs in '(((pno-one (a4 a4 e4 b4 fs4 gs4 gs4 gs4 b4 gs4
                                              e4)))
                                ((pno-two (d5 a4 e5 b4 gs4 gs4 a4 a4 gs4 a4
                                              a4))))
               collect (loop for inst in pchs
                          collect (list (first inst)
                                        (auto-curve-from-indices-and-items
                                         fib-trans-ids (second inst))))))
           (basic-bar '(((6 8) - s s s s s s - - s s s s s s -)))
           (ps-orig '(1 2 3 4 5 2 1 4 3 2 5 4))
           (ps-list (fibonacci-transitions num-bars
                                           (collect-n-rotations 13 ps-orig)))
           (rsp (loop for rs from 1 to 2
                   for psp in (list (list ps-orig) ps-list)
                   collect (list rs (list basic-bar :pitch-seq-palette psp))))
           (rsm `((1 ,(loop for p in '(pno-one pno-two)
                         for rs from 1
                         collect (list p (loop repeat num-bars collect rs))))))
           (nouveau-reich
            (make-slippery-chicken
             '+nouveau-reich+
             :title "Nouveau Reich"
             :ensemble '(((pno-one (piano :midi-channel 1))
                          (pno-two (piano :midi-channel 2))))
             :staff-groupings '(1 1)
             :tempo-map '((1 (q. 72)))
             :set-palette set-pal
             :set-limits-low (first set-limits-lists)
             :set-limits-high (second set-limits-lists)
             :avoid-used-notes nil
             :avoid-melodic-octaves nil
             :set-map `((1 ,fib-trans-ids))
             :rthm-seq-palette rsp
             :rthm-seq-map rsm)))
      (loop for p in '(pno-one pno-two)
         for n in '(("piano one" "pno i") ("piano two" "pno ii"))
         with e = (ensemble nouveau-reich)
         do
           (setf (staff-name (get-data-data p e)) (first n))
           (setf (staff-short-name (get-data-data p e)) (second n)))
      (midi-play nouveau-reich :midi-file "/tmp/nouveau-reich.mid")
      (cmn-display nouveau-reich :file "/tmp/nouveau-reich")
      (write-lp-data-for-all nouveau-reich))

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

    ;;; EOF nouveau-reich.lsp

close

### The code explained

#### The user-defined functions

##### move-first-to-end

The first of the four functions defined for this piece is `move-first-to-end`. This little function "pops" the first item off the list that it is given and appends it again to the end of that list. It is designed for use in the next function, `collect-n-rotations`.

    (defun move-first-to-end (list)
        (let (i)
          (setf i (pop list))
          (setf list (append list (list i)))))

    (move-first-to-end '(1 2 3 4 5))

    => (2 3 4 5 1)

##### collect-n-rotations

The second function defined here collects a list of the new lists created from consecutive calls to `move-first-to-end`. The user specifies an original list and the number of times that list is to be rotated. This function is designed for automatic generation of `pitch-seq` curves that will reflect Reich's technique of phasing.

    (defun collect-n-rotations (num-rotations list-to-rotate)
        (loop repeat num-rotations
           collect list-to-rotate
           do (setf list-to-rotate (move-first-to-end list-to-rotate))))

    (collect-n-rotations 11 '(1 2 3 4 5))

    =>
    ((1 2 3 4 5) (2 3 4 5 1) (3 4 5 1 2) (4 5 1 2 3) (5 1 2 3 4) (1 2 3 4 5)
     (2 3 4 5 1) (3 4 5 1 2) (4 5 1 2 3) (5 1 2 3 4) (1 2 3 4 5))

##### auto-curve-from-indices-and-items

The third user-defined function creates a list of break-point pairs with `x` values that increment by 1. It selects its `y` values from a list of specified items based on the pattern specified in a list of 1-based indices (integers) into that list. The indices must therefore all be between 1 and the length of the list of items, but the list of indices can be of any length, with its indices occurring in any order.

This function is designed for the automatic generation of envelopes for the `set-limits-high` and `set-limits-low` arguments of the `make-slippery-chicken` function. It will ensure that limits are specified for each sequence in the piece, in order to avoid the interpolation of pitches that otherwise occurs when there are fewer break-point pairs than sequences.

    (defun auto-curve-from-indices-and-items (indices items)
        (when (or (<= (apply #'min indices) 0)
                  (> (apply #'max indices) (length items)))
          (error "all indices must be >0 and <= length of items"))
        (loop for pos in indices
           for x from 1
           collect x
           collect (nth (1- pos) items)))

    (auto-curve-from-indices-and-items '(1 4 2 3 1 5 5 1 3 2 2 4) '(a b c d e))

    => (1 A 2 D 3 B 4 C 5 A 6 E 7 E 8 A 9 C 10 B 11 B 12 D)

#### Predefining variables with values for make-slippery-chicken

##### num-bars

The first variable declared, `num-bars`, actually determines the number of sequences that will be make up the piece. However, since the only `rthm-seq` that will be defined is exactly one bar long, the number of sequences is identical to the number of bars. This variable will be used either directly or indirectly to generate the `set-limits-` values, as well as the `rthm-seq-map` and `set-map`.

      (let* ((num-bars 646)

##### set-pal

The next variable, `set-pal`, simply contains the list that will become the `set-palette` of the `slippery-chicken` object to be made. It is declared here, outside of the scope of that function, since the contents of the `fib-trans-ids` variable are dependent on the length of the `set-palette` for their generation.

The chords used in this set palette are the 11 chords used in Steve Reich's Music for 18 Musicians.

             (set-pal '((1 ((fs2 b2 d4 a4 d5 e5 a5 d6)))
                        (2 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6)))
                        (3 ((cs3 fs3 e4 a4 e5 a5 e6)))
                        (4 ((fs2 cs3 e4 a4 b4 e5 a5 b5 e6)))
                        (5 ((d2 a2 e4 fs4 gs4 b4 e5 b5)))
                        (6 ((a2 e3 e4 fs4 gs4 b4 cs5 e5 b5)))
                        (7 ((cs3 fs3 fs4 gs4 a4 cs5 a5 cs6)))
                        (8 ((fs2 cs3 fs4 gs4 a4 b4 cs5 fs5)))
                        (9 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6)))
                        (10 ((d2 a2 fs4 gs4 a4 e5 a5 e6)))
                        (11 ((a2 d2 e4 fs4 a4 e5 a5)))))

##### fib-trans-ids

The `fib-trans-ids` variable uses the `fibonacci-transitions` function to generate a list that is num-bars long from consecutive numbers starting with `1` and ending with the length of the `set-pal` variable. The first purpose of this variable is the automatic generation of a series of set IDs for the `set-map` argument of the `make-slippery-chicken` function. It allows the user to change the number of sets in the `set-palette` and have the list of set IDs used for the `set-map` automatically adjusted accordingly.

The `fib-trans-ids` variable will also be used to generate the lists for `set-limits-high` and `set-limits-low`, as is described in more detail below.

              (fib-trans-ids (fibonacci-transitions
              num-bars
              (loop for i from 1 to (length set-pal) collect i)))

##### set-limits-lists

###### The set-limits- slots and interpolation

The `set-limits-high` and `set-limits-low` slots of the `slippery-chicken` object are designed to confine the pitches available to players in the ensemble (or to the ensemble as a whole) to subsets that gradually change over the course of the piece. They take lists of break-point pairs that are scaled to the number of sequences in the piece, and use them to change the pitch limits for the specified player on a sequence-by-sequence basis. If the list contains fewer break-point pairs than there are sequences in the piece, the pitch limits for the remaining sequences are determined through interpolation. Thus, if a piece has five sequences and the break-point pairs passed to `set-limits-high` for a player consist of `(0 f5 100 a5)`, the upper limit for that player's five sequences would be `f5`, `fs5`, `g5`, `gs5`, and `a5`.

Since the `x` values passed to the `set-limits-` slots are scaled to the number of sequences in the piece, it can be helpful to use actual sequence numbers for these values rather than an arbitrary scale. If this approach were taken, the above list of break-point pairs could be written as `(1 f5 5 a5)` instead.

If the user would like to avoid the interpolation of pitches between sequences, a break-point pair must be given for each individual sequence. If, for example, the user would prefer to have the set limits of the same five bars progress in whole-steps, skipping the `fs` and `gs`, this could be done by indicating `(1 f5 2 f5 3 g5 4 g5 5 a5)`.

The latter is the approach taken for Nouveau Reich, but since there are 646 sequences in the piece, the decision was made to generate these lists of break-point pairs automatically, using a loop to retrieve each sequential sequence number and pair it with an item from a predefined list of pitches.

NB: In order to actually get the pitches `f5`, `g5` etc., these must also be present in the set being used for the current sequence. The `set-limits-` slots can consist of pitches that are not part of the set (and indeed most often will if the interpolating option is used.)

###### One list for both set-limits- slots

In order to avoid repeating code, a loop was defined for Nouveau Reich to generate the lists for both `set-limits-high` and `set-limits-low` in one step. The `make-slippery-chicken` function will later access these sublists by using `(first set-limits-lists)` for the former and and `(second set-limits-lists)` for the latter.

###### Two five-pitch subsets for each set in the palette

Although it is not required that the pitches specified for the set limits are part of the current set, the pitches chosen here for Nouveau Reich are intentionally selected from each set in order to provide a subset of five pitches for each player in each section. This enables an exact mapping of the `pitch-seq` objects defined below, all consisting only of numbers `1` to `5`, to specific consecutive pitches within each subset. Each set in the piece is to be divided into the same two subsets each time it occurs.

The five-pitch subsets are created by always giving the `pno-one` player the top five pitches of each set and the `pno-two` player the bottom five pitches of the same set, which may or may not overlap with those of the `pno-one`. This means that `set-limits-low` values are only required for `pno-one` (always specifying the fifth pitch from the top) and `set-limits-high` values are only required for `pno-two` (always specifying the fifth pitch from the bottom).

NB: The exact mapping of pitches to `pitch-seqs` also requires use of the `:avoid-used-notes` and `:avoid-melodic-octaves` keywords of the `make-slippery-chicken` function, as described in more detail below.

###### Outer loop pairs players with pitches and determines form

The outer loop in the `set-limits-lists` therefore specifies lists of 11 pitches for each player, corresponding to sets 1 to 11. These lists are each paired with the appropriate player ID. Additional parentheses are added in order to ensure that the form is complete and can be passed directly to the `:set-limits-` keywords of the `make-slippery-chicken` function.

             (set-limits-lists
              (loop for pchs in '(((pno-one (a4 a4 e4 b4 fs4 gs4 gs4 gs4 b4 gs4
                                                e4)))
                                  ((pno-two (d5 a4 e5 b4 gs4 gs4 a4 a4 gs4 a4
                                                a4))))

###### Implementing the auto-curve-from-indices-and-items function

The inner loop uses the `auto-curve-from-indices-and-items` function defined above to generate a list of break-point pairs whose `x` values start at `1` and increment by `1` for the number of items in the list of indices. For its list of indices, it uses the same list generated for the `fib-trans-ids` variable, thereby ensuring that there are the same number of pitch limits as there are sets in the set map, and that the same pitch limits are always paired with the same sets from the `set-palette`.

                 collect (loop for inst in pchs
                            collect (list (first inst)
                                          (auto-curve-from-indices-and-items
                                           fib-trans-ids (second inst))))))

##### basic-bar, ps-orig, ps-list, and rsp

The next four variables are used to algorithmically construct the `rthm-seq-palette`, using the function `collect-n-rotations` defined above.

###### basic-bar

Leaning on the phasing technique found in Steve Reich's Piano Phase, the only rhythmic material for this piece will consist of 12 sixteenth notes in a 6/8 bar. This bar is defined once as `basic-bar`.

             (basic-bar '(((6 8) - s s s s s s - - s s s s s s -)))

###### Reich's principle of phasing

Reich's principal of phasing consists of having two players play the same rhythmic pattern and/or melodic contour, initially in unison, and gradually changing the second player's part by shifting the pattern in only that player's part by one unit at a time. The first unit in the pattern is taken off the front of the pattern and moved to the end, thus taking it more and more "out of phase" with each rotation. One of the fascinating by-products of the phasing technique are what Reich calls resulting patterns: new melodic patterns and sensations of changing meter that emerge at each shift.

###### Non-phasing and phasing pitch-seqs

Since the rhythmic pattern in this piece consists of consecutive sixteenths, only a shift of the melodic contour will produce an audible phasing effect. The code for this piece therefore defines an original melodic contour, as `ps-orig`, and then a collection of gradually shifting permutations of that contour using the `collect-n-rotations` function defined above in combination with `fibonacci-transitions`.

The `collect-n-rotations` function is first used to collect a list of 13 rotations of the original `pitch-seq`, meaning the players will start and end with "unison" melodic contours, and the second player will gradually pass through all possible rotations of the original `pitch-seq` in the course of the piece. This list of 13 rotations is then passed to the `fibonacci-transitions` function with the `num-bars` argument to generate a list of `pitch-seq` curves that has one `pitch-seq` for each sequence.

             (ps-orig '(1 2 3 4 5 2 1 4 3 2 5 4))
             (ps-list (fibonacci-transitions num-bars
                                             (collect-n-rotations 13 ps-orig)))

###### Generating the rthm-seq-palette

A short loop is then used to construct a `rthm-seq-palette` consisting of only two `rthm-seqs`. At each pass through the loop, a `rthm-seq`, with ID `1` or `2`, is created by combining the `basic-bar` with a different `pitch-seq-palette`. The `ps-orig` is used as the sole `pitch-seq` in the `pitch-seq-palette` for the first `rthm-seq`, and the `ps-list` is used for the second. (Also see the page on using multiple curves in the same pitch-seq-palette).

             (rsp (loop for rs from 1 to 2
                     for psp in (list (list ps-orig) ps-list)
                     collect (list rs (list basic-bar :pitch-seq-palette psp))))

##### Generating the rthm-seq-map

The final bit of content to be generated for the `make-slippery-chicken` function is the `rthm-seq-map`. This too is done using a small loop, this time collecting a list of multiple repetitions of either `1`, `2`, or `nil` for the players `pno-one`, `pno-two`, and `pno-two-lh`.

This variable is defined using Lisp's backquote and comma reader macros, allowing the code to be formulated more concisely. More on these macros can be found in the entry for backquote at the Common Lisp HyperSpec.

             (rsm `((1 ,(loop for p in '(pno-one pno-two pno-two-lh)
                           for rs in '(1 2 nil)
                           collect (list p (loop repeat num-bars collect rs))))))

##### The call to make-slippery-chicken within a variable

The last variable to be declared within the `let*` expression is `nouveau-reich`. This variable will contain the entire `slippery-chicken` object generated by calling the `make-slippery-chicken` function, using the other variables defined above as the values for its arguments. Compared to the code for Primary Disposition and Second Law, this call to `make-slippery-chicken` looks very sleek.

This piece introduces two new keyword arguments of the `make-slippery-chicken` function, namely `avoid-used-notes` and `avoid-melodic-octaves`, which are both required for the exact mapping of the `pitch-seqs` of `ps-orig` and `ps-list` to the 5-pitch subsets defined above.

             (nouveau-reich
              (make-slippery-chicken
               '+nouveau-reich+
               :ensemble '(((pno-one (piano :midi-channel 1))
                            (pno-two (piano :midi-channel 2))
                            (pno-two-lh (piano-lh :midi-channel 2))))
               :staff-groupings '(1 2)
               :tempo-map '((1 (q. 72)))
               :set-palette set-pal
               :set-limits-low (first set-limits-lists)
               :set-limits-high (second set-limits-lists)
               :avoid-used-notes nil
               :avoid-melodic-octaves nil
               :set-map `((1 ,fib-trans-ids))
               :rthm-seq-palette rsp
               :rthm-seq-map rsm)))

###### avoid-used-notes

By default, slippery chicken's automatic pitch-selection algorithm endeavors to prevent the same pitch from being assigned to more than one player in any given sequence. However, as some of the subsets created by the `set-limits-lists` loop overlap in their pitch content, this would result in some of those subsets having fewer than five pitches. In order to ensure that each of the subsets has five pitches, even if some of those subsets contain the same pitches for the same sequence, so that the `pitch-seq` numbers can be exactly mapped to those pitches, the `avoid-used-notes` keyword can be set to `NIL`.

               :avoid-used-notes nil

###### avoid-melodic-octaves

Another default feature of slippery chicken's pitch-selection algorithm is the avoidance of melodic octaves. This feature will prevent two non-unison pitches of the same pitch-class from occurring in sequence in any player's part. This rule, too, will prevent the exact mapping of `pitch-seq` numbers to pitches of the five-pitch subsets. For this piece, therefore, this feature is also disabled by setting the `avoid-melodic-octaves` keyword of the `make-slippery-chicken` function to `NIL`.

             :avoid-melodic-octaves nil

#### Changing staff names in an existing sc object

Since both players in the Nouveau Reich ensemble use the same `instrument` object of the `+slippery-chicken-standard-instrument-palette+`, they will, by default, each have the same full and abbreviated staff names in the score. This can be remedied by changing the value of the `staff-name` and `staff-short-name` slots of the corresponding `instrument` objects within the `ensemble` object stored in the `slippery-chicken` object's `ensemble` slot. The data in this `ensemble` object is copied from and separate to that of the `instrument-palette`, and can therefore be changed independently. This is done here using a loop after the `slippery-chicken` object has been made.

      (loop for p in '(pno-one pno-two)
         for n in '(("piano one" "pno i") ("piano two" "pno ii"))
         with e = (ensemble nouveau-reich)
         do
           (setf (staff-name (get-data-data p e)) (first n))
           (setf (staff-short-name (get-data-data p e)) (second n)))

Since 28/5/14 Staff names can now also be set when initialising a slippery-chicken object, e.g.:

    (make-slippery-chicken
     '+mini+
     :ensemble '(((vn1 (violin :midi-channel 1 :staff-names "violin I"))
                  (vn2 (violin :midi-channel 2 :staff-names "violin II"))
                  (vc (cello :midi-channel 3))))
     :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
     :set-map '((1 (1 1 1 1 1)))
     :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                             :pitch-seq-palette ((1 2 3)))))
     :rthm-seq-map '((1 ((vn1 (1 1 1 1 1))
                         (vn2 (1 1 1 1 1))
                         (vc (1 1 1 1 1))))))))

However, do see this note for information about Lilypond and instrument names.

#### The output

With all this done, the output for Nouveau Reich can be easily generated in the same manner as for Primary Disposition and Second Law. As this piece has 646 bars and several internal loops for generating data that is processed for each of those bars, the generation time can be quite long. The result is a 28-page LilyPond score, a 41-page CMN score, and an 18-minute MIDI file.

      (midi-play nouveau-reich :midi-file "/tmp/nouveau-reich.mid")
      (cmn-display nouveau-reich :file "/tmp/nouveau-reich")
      (write-lp-data-for-all nouveau-reich))