"Second Law" — Intra-phrasal looping

+ Associated files

close


NB: An exercise relating to the material covered in this tutorial can be found on the Exercises page.

This document assumes that you having familiarized yourself with the core usage of slippery chicken. It provides an introduction to generating some of the material for slippery chicken algorithmically, using the chop method and the fibonacci-transitions function to create intra-phrasal looping, in addition to integrating separate Lisp routines.

Intra-phrasal looping is a compositional process made possible by slippery chicken and is a technique that demonstrates the strengths of using slippery chicken as a compositional tool. It allows the user to produce an entire, motivically coherent and structurally sound composition from a small amount of source material and a few concisely defined processes, even automatically incorporating musical elements such as orchestration and larger-scale structure.

+ 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.

(let* ((sl-rsp-orig ; defining the source material
        (make-rsp
         'sl-rsp
         '((1 
            ;; A backslash (\) must be used as an escape character when
            ;; indicating a tie to the previous note using a + symbol before a
            ;; numerically represented rhythmic value (as opposed to a letter,
            ;; such as 's or 'e). (This is because Lisp will otherwise read
            ;; "+32" as "positive 32".)
            ((((4 4) - (e.) s - - \+32 32 32 32 (e) - 
               - 32 32 (s) 32 32 32 (32) - - (s) s s (s) - )
              (- (e..) 32 - +q q (q))
              (h. (q)))
             :pitch-seq-palette ((1 2 1 2 3 2 3 3 1 2 5 5 7 6)
                                 (2 1 2 1 2 3 2 2 4 3 2 2 1 2)
                                 (5 3 4 5 4 5 4 5 2 3 6 6 8 7))
             :marks (a 1 s 1 slur 3 5 a 6 slur 6 7 slur 8 10 a 11 s 11 12 
                       a 13))))))
       
       ;; fragmenting the source material
       (sl-rsp-chopped (chop sl-rsp-orig 
                             '((1 2) (1 1) (2 2)) ; chop points
                             'e)) ; chopping unit
       
       ;; setting the measure structure
       (num-seqs-list '(53 61 97 79 89 73)))
 
  ;; adjusting the instrument attributes
  (loop for i in 
         '((flute 13)
           (oboe 7)
           (b-flat-clarinet 9)
           (bassoon 7)
           (french-horn 5)
           (b-flat-trumpet 7)
           (tenor-trombone 5)
           (double-bass 5))
     do
       (set-slot 'largest-fast-leap 
                 (second i)
                 (first i)
                 +slippery-chicken-standard-instrument-palette+))
  

  ;; This function prints the results of the chop method in easy-to-read form 
  ;; (print-simple sl-rsp-chopped)

  ;; calling slippery-chicken
  (make-slippery-chicken
   '+second-law+
   :title "Second Law"
   :instrument-palette +slippery-chicken-standard-instrument-palette+
   :ensemble '(((fl (flute :midi-channel 1))
                (ob (oboe :midi-channel 2))
                (cl (b-flat-clarinet :midi-channel 3))
                (bn (bassoon :midi-channel 4))
                (hn (french-horn :midi-channel 5))
                (tp (b-flat-trumpet :midi-channel 6))
                (tb (tenor-trombone :midi-channel 7))
                (vno (violin :midi-channel 8))
                (vnt (violin :midi-channel 9))
                (va (viola :midi-channel 12))
                (vc (cello :midi-channel 13))
                (cb (double-bass :midi-channel 14))))

   ;; setting pitch ranges for the scope of this composition
   :set-limits-high '((cl (0 c6 100 c6))
                      (vc (0 a4 100 a4))
                      (cb (0 f3 100 f3)))
   :staff-groupings '(4 3 5)
   :tempo-map '((1 (q 69)))
   :set-palette '((1 ((c3 g3 cs4 e4 fs4 a4 bf4 c5 d5 f5 gf5 af5 ef6)))
                  (2 ((c3 fs3 cs4 e4 g4 a4 b4 c5 df5 f5 g5 af5 ef6)))
                  (3 ((d3 f3 cs4 e4 fs4 a4 b4 c5 d5 e5 fs5 af5 ef6)))
                  (4 ((d3 e3 cs4 ef4 fs4 a4 b4 c5 d5 e5 fs5 af5 d6)))
                  (5 ((d3 e3 g3 a3 c4 ef4 f4 af4 bf4 cs5 fs5 b5 df6)))
                  (6 ((c3 d3 gf3 af3 b3 e4 a4 df5 ef5 g5 bf5 df6)))
                  (7 ((b2 e3 fs3 as3 ef4 g4 a4 d5 f5 af5 c6 df6)))
                  (8 ((af2 b2 ef3 fs3 as3 cs4 e4 g4 a4 d5 f5 bf5 c6 e6 af6)))
                  (9 ((af2 b2 ef3 fs3 bf3 d4 f4 a4 cs5 e5 g5 c6 f5 af6)))
                  (10 ((af2 c3 ef3 fs3 bf4 d4 f4 a4 cs5 e5 g5 b5 fs6))))
   
   ;; using a Lisp routine to generate the set-map
   :set-map (loop for section in 
                 '((1 (1 2 3)) 
                   (2 (2 3 4 1))
                   (3 (1 3 5 6 7))
                   (4 (8 9))
                   (5 (5 6 7 9 3))
                   (6 (9 10)))
               collect
                 (list (first section)
                       (fibonacci-transitions 
                        (nth (1- (first section)) num-seqs-list)
                        (second section))))

   :rthm-seq-palette sl-rsp-chopped
   
   ;; using a Lisp routine to generate the rthm-seq-map
   :rthm-seq-map (loop for section in
                      '((((1 3) fl ob ))
                        (((3 4) fl ob cl))
                        (((5 6 7 8) fl ob cl)
                         ((11 12 13 14) bn tb vc cb))
                        (((9 10 11) hn tp))
                        (((15 16 25 26) fl ob vno vnt)
                         ((9 10 13 14) cl hn va)
                         ((3 1 16 3) tp tb)
                         ((12 13 10 11) bn vc cb))
                        (((1 3 4) fl ob cl bn hn tp tb vno vnt va vc cb)))
                    for section-num from 1
                    collect 
                      (list section-num
                            (loop for ins-group in section 
                               appending
                                 (loop with fts =
                                      (loop for ch in (first ins-group) 
                                         collect
                                           (list 1 ch))
                                    for ins in (rest ins-group) 
                                    collect
                                      (list ins
                                            (fibonacci-transitions
                                             (nth (1- section-num)
                                                  num-seqs-list) 
                                             fts))))))))

;; The re-bar method can be applied outside of the above "let*" scope, since
;; the slippery-chicken class produces a global variable from its first
;; argument
(re-bar +second-law+
        :min-time-sig '(4 4) 
        :auto-beam 'q)

;;; the output:
;;; midi
(midi-play +second-law+ :midi-file "/tmp/second-law.mid")

;; CMN output
(cmn-display +second-law+ :file "/tmp/second-law.eps" :size 12)

;; LP output
(write-lp-data-for-all +second-law+ :base-path "/tmp/")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF

close

The code explained

The material for the rthm-seq-palette, the rthm-seq-map, and the set-map for this composition is generated algorithmically. Two of the options for algorithmically generating material are to place the generating code directly into the slippery-chicken object, as can be seen in the set-map and rthm-seq-map blocks, or to assign the generating code to variables, as has been done here at the top of the example.

Setting up the environment

As in the core usage tutorial, this example first sets the global parameters for package and scale:

(in-package :sc)

(in-scale :chromatic)

Setting up the scope

Setting the variables

The variables in this example are declared using the let macro and enclosed in the same scope as the call to the make-slippery-chicken function below them, allowing the material assigned to these variables to be used in that function. (Another option would be to declare global variables for the material, but the let option is more concise.)

Three variables are declared here. The first, sl-rsp-orig, stores the rthm-seq-palette that serves as the source material for this piece. The second, sl-rsp-chopped, stores the results of applying the chop method to the contents of the sl-rsp-orig variable. The chop method itself returns a rthm-seq-palette object, and can therefore be directly passed to the :rthm-seq-palette keyword argument within the subsequent call to the make-slippery-chicken function. This method will be described in more detail below. The third variable, num-seqs-list, is a list of numbers that will be the number of rthm-seq objects and sets in each of their respective maps within the slippery-chicken object, thereby determining the length of each section.

The original rthm-seq-palette

The original palette is constructed in essentially the same way as in the core usage guide, with one new attribute, namely that this example defines more than one curve (pitch-seq) for the pitch-seq-palette.

             :pitch-seq-palette ((1 2 1 2 3 2 3 3 1 2 5 5 7 6)
                                 (2 1 2 1 2 3 2 2 4 3 2 2 1 2)
                                 (5 3 4 5 4 5 4 5 2 3 6 6 8 7))

See the page on using multiple curves in the same :pitch-seq-palette for more detail on this feature.

Adjusting instrument attributes for the scope of the piece

The last element before the creation of the slippery-chicken object is a short routine that adjusts the largest-fast-leap value of the instrument objects used in the composition.

  (loop for i in 
       '((flute 13)
         (oboe 7)
         (b-flat-clarinet 9)
         (bassoon 7)
         (french-horn 5)
         (b-flat-trumpet 7)
         (tenor-trombone 5)
         (double-bass 5))
     do
       (set-slot 'largest-fast-leap 
                 (second i)
                 (first i)
                 +slippery-chicken-standard-instrument-palette+))

This loop passes through a list of instrument names paired with numbers and sets the largest-fast-leap value of the corresponding instrument object in the global +slippery-chicken-standard-instrument-palette+ (which is the instrument-palette used in this piece) to the given number. Note that the instrument names here are the IDs in the given instrument-palette, not the user-defined player IDs in the ensemble block.

This routine limits the maximum linear interval between two consecutive 32nd-notes for each of the instruments in the list to a relatively small span (mostly perfect fourths and fifths), ensuring that the resulting music is more easily playable.

More information on the largest-fast-leaps and other slots of the instrument class can be found in the source documentation here. Also see the page on tailoring instrument definitions for related information.

The call to make-slippery-chicken

Many of the components of the make-slippery-chicken function for this piece are the same as in the core-usage guide. Thus, the usage of the initial global variable, as well as the title, instrument-palette, ensemble, staff-groupings, and tempo-map blocks will not be discussed here.

Setting tessitura for one piece

Although the set-slot method used above could be used here as well to set the upper limits for instrument ranges for the entirety of the given piece, this example uses the :set-limits-high keyword argument of the make-slippery-chicken function to cap the upper limits of the clarinet, cello, and bass for the duration of the piece:

   :set-limits-high '((cl (0 c6 100 c6))
                      (vc (0 a4 100 a4))
                      (cb (0 f3 100 f3)))

See the page on pitches for more details on the usage of :set-limits-high and :set-limits-low.

Well-considered set palettes

The set-palette for this example is constructed by hand and not algorithmically. Emphasis was placed on choosing pitches that are seldom more than a third apart, to allow for smoother lines in the instruments.

   :set-palette '((1 ((c3 g3 cs4 e4 fs4 a4 bf4 c5 d5 f5 gf5 af5 ef6)))
                  (2 ((c3 fs3 cs4 e4 g4 a4 b4 c5 df5 f5 g5 af5 ef6)))
                  (3 ((d3 f3 cs4 e4 fs4 a4 b4 c5 d5 e5 fs5 af5 ef6)))
                  (4 ((d3 e3 cs4 ef4 fs4 a4 b4 c5 d5 e5 fs5 af5 d6)))
                  (5 ((d3 e3 g3 a3 c4 ef4 f4 af4 bf4 cs5 fs5 b5 df6)))
                  (6 ((c3 d3 gf3 af3 b3 e4 a4 df5 ef5 g5 bf5 df6)))
                  (7 ((b2 e3 fs3 as3 ef4 g4 a4 d5 f5 af5 c6 df6)))
                  (8 ((af2 b2 ef3 fs3 as3 cs4 e4 g4 a4 d5 f5 bf5 c6 e6 af6)))
                  (9 ((af2 b2 ef3 fs3 bf3 d4 f4 a4 cs5 e5 g5 c6 f5 af6)))
                  (10 ((af2 c3 ef3 fs3 bf4 d4 f4 a4 cs5 e5 g5 b5 fs6))))

Another important attribute of this particular set-palette definition is that the IDs of each of the sets are consecutive integers. Although these IDs could be numbers or alphabetic symbols, the use of numbers facilitates the implementation of the fibonacci-transitions function below.

See the page on pitches for more details on the usage of set-palettes.

An application of fibonacci-transitions

The fibonacci-transitions function is used several times in this composition (see the page Fibonacci Transitions for a general introduction to the function). It occurs for the first time in the generation of the set-map, in conjunction with the variable num-seqs-list.

       (num-seqs-list '(53 61 97 79 89 73)))

   :set-map (loop for section in 
                 '((1 (1 2 3)) 
                   (2 (2 3 4 1))
                   (3 (1 3 5 6 7))
                   (4 (8 9))
                   (5 (5 6 7 9 3))
                   (6 (9 10)))
               collect
                 (list (first section)
                       (fibonacci-transitions 
                        (nth (1- (first section)) num-seqs-list)
                        (second section)))) 

For each number-list pair in the loop, the fibonacci-transitions function is called and applied to the list of numbers of that pair (accessed by (second section)) according to the number of times stipulated by the corresponding nth element of the num-seqs-list. The result is then collected into a list with the corresponding section number (accessed by (first section)) and passed to the set-map keyword argument of the call to slippery-chicken. The loop eliminates the need for a series of individual calls to fibonacci-transitions that would look like:

      (fibonacci-transitions 53 '(1 2 3))
      (fibonacci-transitions 61 '(2 3 4 1))
      etc.

Its result, if typed in manually instead, would look like this:

((1
  (1 1 1 1 1 1 2 1 1 1 1 2 1 1 2 1 2 2 2 1 2 1 2 2 2 2 2 2 2 3 2 2 3 2 3 3
   3 2 3 2 3 3 3 2 3 3 3 3 3 3 3 3 3))
 (2
  (2 2 2 2 2 3 2 2 2 2 3 2 2 3 2 3 2 3 2 3 3 3 3 3 4 3 4 3 4 3 4 3 4 3 4 4
   4 4 4 1 4 1 4 1 4 1 4 1 4 1 1 1 4 1 1 1 1 1 1 1 1))
 (3
  (1 1 1 1 1 1 1 3 1 1 1 1 3 1 1 3 1 3 1 3 1 3 1 3 1 3 3 3 3 3 3 3 5 3 3 5
   3 5 3 5 3 5 3 5 3 5 5 5 5 5 5 5 6 5 5 6 5 6 5 6 6 5 6 5 6 6 6 6 6 6 6 7
   6 6 7 6 7 6 7 7 6 7 6 7 7 7 6 7 7 7 7 7 7 7 7 7 7))
 (4
  (8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 8 8 8 8 8 8 8 9 8 8 8 8 9 8 8 9 8 8 9 8 9
   8 9 8 9 9 8 9 8 9 9 8 9 9 8 9 9 9 9 9 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
   9 9 9 9 9 9 9))
 (5
  (5 5 5 5 5 5 6 5 5 5 5 6 5 5 6 5 6 6 6 5 6 5 6 6 6 6 6 6 6 7 6 6 7 6 7 7
   7 6 7 6 7 7 7 7 7 7 7 9 7 7 9 7 9 9 9 7 9 7 9 9 9 9 9 9 9 3 9 9 3 9 3 3
   3 9 3 9 3 3 3 9 3 3 3 3 3 3 3 3 3))
 (6
  (9 9 9 9 9 9 9 9 9 9 9 9 9 10 9 9 9 9 9 9 9 10 9 9 9 9 10 9 9 10 9 9 10
   9 10 9 10 10 9 10 9 10 9 10 10 9 10 10 10 10 10 9 10 10 10 10 10 10 10
   10 10 10 10 10 10 10 10 10 10 10 10 10 10)))

This short loop has automatically generated a considerable amount of data and made its representation and inclusion in the example conceptually concise, easy to enter and modify, and tidy.

An application of the chop method

For this composition, the chop method is employed to break the original rthm-seq-palette into fragments for use with fibonacci-transitions. (See the chop page for a more general introduction to the method.) The method is applied here to create fragments whose durations are multiples of an eighth note using the following arguments, then assigning the resulting rthm-seq-palette to the variable sl-rsp-chopped:

       (sl-rsp-chopped (chop sl-rsp-orig 
                             '((1 2) (1 1) (2 2)) ; chop points
                             'e)) ; chopping unit

The chop points here are stipulated specifically, dividing the basis beat of the original material (which is determined by the time signature of the original material and is a quarter note in this instance) into three fragments, two that are one unit long (one beginning and ending on the first eighth of the beat and one beginning and ending on the second eighth of the beat), and one that is two units long (beginning on the first eighth of the beat and ending on the second eighth of the beat). The method applies this chopping pattern successively to each beat in the entire original rthm-seq-palette, resulting in this example in 36 fragments, the first twelve of which have the following structure:

;; These are first 12 results of the (print-simple sl-rsp-chopped) function
;; commented out in the source code above. 

rthm-seq 1
(1 4): rest E., note S, 
rthm-seq 2
(1 8): rest 8, 
rthm-seq 3
(1 8): rest S, note S, 
rthm-seq 4
(1 4): rest 32, note 32, note 32, note 32, rest E, 
rthm-seq 5
(1 8): rest 32, note 32, note 32, note 32, 
rthm-seq 6
(1 8): rest 8, 
rthm-seq 7
(1 4): note 32, note 32, rest S, note 32, note 32, note 32, rest 32, 
rthm-seq 8
(1 8): note 32, note 32, rest S, 
rthm-seq 9
(1 8): note 32, note 32, note 32, rest 32, 
rthm-seq 10
(1 4): rest S, note S, note S, rest S, 
rthm-seq 11
(1 8): rest S, note S, 
rthm-seq 12
(1 8): note S, rest S,
etc.

The results of the chop method are assigned to the variable sl-rsp-chopped and passed to the rthm-seq-palette.

   :rthm-seq-palette sl-rsp-chopped

The individual fragments within the resulting rthm-seq-palette are then accessed using the fibonacci-transitions function from within the rhtm-seq-map, as detailed below.

Combining chop and fibonacci-transitions for
intra-phrasal looping

The :rthm-seq-map and orchestration

In the next block of this example, the rthm-seq-map is then also generated algorithmically:

   :rthm-seq-map (loop for section in
                      '((((1 3) fl ob ))
                        (((3 4) fl ob cl))
                        (((5 6 7 8) fl ob cl)
                         ((11 12 13 14) bn tb vc cb))
                        (((9 10 11) hn tp))
                        (((15 16 25 26) fl ob vno vnt)
                         ((9 10 13 14) cl hn va)
                         ((3 1 16 3) tp tb)
                         ((12 13 10 11) bn vc cb))
                        (((1 3 4) fl ob cl bn hn tp tb vno vnt va vc cb)))
                    for section-num from 1
                    collect 
                      (list section-num
                            (loop for ins-group in section 
                               appending
                                 (loop with fts =
                                      (loop for ch in (first ins-group) 
                                         collect
                                           (list 1 ch))
                                    for ins in (rest ins-group) 
                                    collect
                                      (list ins
                                            (fibonacci-transitions
                                             (nth (1- section-num)
                                                  num-seqs-list) 
                                             fts))))))

In addition to being the code that creates the intra-phrasal looping, this loop also determines one aspect of the orchestration for the piece, in that it defines which instruments of the ensemble play in each section, and which of those instruments are "coupling" each other by playing rhythmically identical material.

The loop passes through a list of nested lists. The first level of these contains six lists, each corresponding to one section of the piece's section map. The next level of lists determines how many separate rhythmic patterns will be simultaneously played in each section. Sections 1, 2 and 4, for example, each have only one rhythmic element played by all instruments, while section 5 has four rhythmic elements played by different instrument groups. The final level determines exactly which rhythmic patterns will be played, and which instruments will play each of these patterns, by specifying a sublist of numbers and a series of player IDs.

      '((((1 3) fl ob))
        (((3 4) fl ob cl))
        (((5 6 7 8) fl ob cl)
         ((11 12 13 14) bn tb vc cb))
        (((9 10 11) hn tp))
        (((15 16 25 26) fl ob vno vnt)
         ((9 10 13 14) cl hn va)
         ((3 1 16 3) tp tb)
         ((12 13 10 11) bn vc cb))
        (((1 3 4) fl ob cl bn hn tp tb vno vnt va vc cb)))

The player IDs are taken from the ensemble block. The numbers are the IDs of the individual rthm-seq objects contained in the rthm-seq-palette created by the chop method.

Assembling the rthm-seq-map from the chop fragments

To accommodate for the additional level of nesting that the chop method adds to any rthm-seq-palette it processes, the loop function here automatically adds the additional ID from the original rthm-seq object required to reference each given rthm-seq (fragment) that the chopped palette contains. Since only one rthm-seq was defined in the original rthm-seq-palette, and this was given the ID 1, the newly created fragments are accessed using (1 1), to get the first fragment of rthm-seq 1, (1 2) to get the second fragment of rthm-seq 1, (1 3) to get the third fragment of rthm-seq 1, etc.

      (loop for ch in (first ins-group) 
         collect
           (list 1 ch))

See the manual page on chop for more detail on this concept.

The last component of this loop is then a call to the fibonacci-transitions function for each of the players listed, using the references created from the collection of the original rthm-seq ID and the new rthm-seq IDs, and the same num-seqs-list used for the set-map.

      collect
        (list ins
              (fibonacci-transitions
               (nth (1- section-num) num-seqs-list) 
               fts))))))

If these were written out in full, they would look like this:

 ((1
   ((fl (fibonacci-transitions 53 '((1 1) (1 3))))
    (ob (fibonacci-transitions 53 '((1 1) (1 3)))))
  (2
   ((fl (fibonacci-transitions 61 '((1 3) (1 4))))
    (ob (fibonacci-transitions 61 '((1 3) (1 4))))
    (cl (fibonacci-transitions 61 '((1 3) (1 4))))))
    etc.

All together, then, the code that this loop generates would begin like this:

((1
  ((FL
    ((1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 3) (1 1)
     (1 1) (1 1) (1 1) (1 3) (1 1) (1 1) (1 3) (1 1) (1 1) (1 3) (1 1)
     (1 3) (1 1) (1 3) (1 1) (1 3) (1 3) (1 1) (1 3) (1 3) (1 1) (1 3)
     (1 3) (1 3) (1 3) (1 3) (1 1) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)
     (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)))
   (OB
    ((1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 1) (1 3) (1 1)
     (1 1) (1 1) (1 1) (1 3) (1 1) (1 1) (1 3) (1 1) (1 1) (1 3) (1 1)
     (1 3) (1 1) (1 3) (1 1) (1 3) (1 3) (1 1) (1 3) (1 3) (1 1) (1 3)
     (1 3) (1 3) (1 3) (1 3) (1 1) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)
     (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)))))
 (2
  ((FL
    ((1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)
     (1 4) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 4) (1 3) (1 3)
     (1 3) (1 3) (1 4) (1 3) (1 3) (1 4) (1 3) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 3) (1 4) (1 4) (1 3) (1 4) (1 4) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)))
   (OB
    ((1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)
     (1 4) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 4) (1 3) (1 3)
     (1 3) (1 3) (1 4) (1 3) (1 3) (1 4) (1 3) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 3) (1 4) (1 4) (1 3) (1 4) (1 4) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)))
   (CL
    ((1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3)
     (1 4) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 3) (1 4) (1 3) (1 3)
     (1 3) (1 3) (1 4) (1 3) (1 3) (1 4) (1 3) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 3) (1 4) (1 4) (1 3) (1 4) (1 4) (1 4) (1 4) (1 4) (1 3)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)
     (1 4) (1 4) (1 4) (1 4) (1 4) (1 4)))))
    etc.
Equal lengths

A final reminder must be made before leaving this section. As discussed in the core-usage guide, all simultaneously occurring rthm-seq objects in a given rthm-seq-map must be of the same length. For example, the fifth section from this piece includes fibonacci-based transitions from rthm-seq (1 15) to (1 16) to (1 25) to (1 26) in the fl, ob, vno and vnt, against simultaneously occurring transitions from rthm-seq (1 3) to (1 1) to (1 16) to (1 3) in the tp and tb. This only functions without returning an error because the corresponding sections are of equal length, thus (1 15) and (1 3) both 1/8 bars, (1 16) and (1 1) are both 1/4 bars, etc. (This holds true for the other simultaneously occurring rhythmic material in that section as well.)

Calling functions for editing material generated by slippery chicken

re-bar

The final method called in this example before generating the output is the re-bar method, which re-groups the existing musical material into new bars as close to the specified length as possible. The chopped material from this example produces only 1/4 and 1/8 bars, so re-barring them into larger measures, in this instance, bars that are 4/4 or larger, will make the resulting music easier to read. That is done by calling the re-bar method with the global variable assigned to the musical material generated above (+second-law+) and specifying the :min-time-sig as '(4 4). This example also turns on the :auto-beam function, setting it to automatically beam the newly barred content into quarters ('q).

(re-bar +second-law+
        :min-time-sig '(4 4) 
        :auto-beam 'q)

As seen here, the re-bar method can be applied outside of the above let scope, since the slippery-chicken class produces a global variable from its first argument.

The output

The output from this piece is generated in the same manner described in the core-usage guide, with the one addition of the :size argument to the cmn-display method.

;;; midi
(midi-play +second-law+ :midi-file "/tmp/second-law.mid")

;; CMN output
(cmn-display +second-law+ :file "/tmp/second-law.eps" :size 12)

;; LP output
(write-lp-data-for-all +second-law+ :base-path "/tmp/")

NB: slippery chicken has a number of built-in algorithms for automatic tidying, such as respell-notes, which attempts to automatically choose the best enharmonic spelling of pitches in the printed output. The respell-notes algorithm is called automatically within the cmn-display and write-lp-data-for-all methods. This can be disabled by specifying :respell-notes nil in the call to cmn-display or write-lp-data-for-all. If the user finds that the decisions slippery chicken makes are not in line with his or her own preferences, the post-generation editing methods can be used to manually tweak the output after the fact, such as by changing enharmonic spellings etc. The output for this tutorial has not been tweaked. See the page on post-generation data editing for more on this.


The :size argument of the cmn-display method scales the size of the music on the page. The use of this argument is necessary in this example, as the music would not fit on the page otherwise, and the following warning would be issued by slippery chicken.

WARNING:
   we don't have room on the page for this score: try a font size less than 13:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF