"Tempus Perfectum" — Composing with L-Systems
+ 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
- An MP3 of the first computer ("tape") part
- An MP3 of the second computer ("tape") part
NB: An exercise relating to the material covered in this tutorial can be found on the Exercises page.
This demo piece, Tempus Perfectum, and its accompanying tutorial, will explore the use of L-systems to generate material for slippery chicken compositions. It also provides a concrete example of the use of slippery chicken with Common Lisp Music (CLM).
More detail is available for both of these slippery chicken features on the L-systems and slippery chicken and CLM pages of the manual.
+ 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* ((num-seqs 71)
       (src-width 50)
       (sndfile-dir
        (concatenate 'string
                     cl-user::+slippery-chicken-home-dir+
                     "doc/manual/resources/"))
       (ens '(((ob (oboe :midi-channel 1))
               (cl (b-flat-clarinet :midi-channel 2))
               (bn (bassoon :midi-channel 3))
               (hn (french-horn :midi-channel 4))
               (tp (c-trumpet :midi-channel 5))
               (tb (tenor-trombone :midi-channel 6))
               (pr (piano :midi-channel 7))
               (pl (piano-lh :midi-channel 8))
               (vn (violin :midi-channel 9))
               (va (viola :midi-channel 11))
               (vc (cello :midi-channel 12))
               (c1 (computer :midi-channel 13))
               (c2 (computer :midi-channel 14)))))
       (seqs-rules (loop repeat (length (first ens))
                      with l = '(1 2 3 4 5 6 7 8)
                      for p from 0
                      with po = 1
                      with s = nil
                      do (setf s (list (nth (mod p 8) l)
                                       (nth (mod (+ p po) 8) l)
                                       (nth (mod (+ p (* 2 po)) 8) l)
                                       (nth (mod (+ p (* 3 po)) 8) l)))
                      collect (list (1+ p) s)
                      when (= 8 (first s))
                      do (setf po (1+ po))))
       (seqs (make-l-for-lookup 'l-seqs
                                '((1 ((1)))
                                  (2 ((2)))
                                  (3 ((3)))
                                  (4 ((4)))
                                  (5 ((5)))
                                  (6 ((6)))
                                  (7 ((7)))
                                  (8 ((8))))
                                seqs-rules))
       (rsm-lists 
        (loop for s from 1 to (length (first ens))
           for p in (loop for i in (first ens)
                       collect (first i))
           collect (list p (flatten (do-simple-lookup seqs s num-seqs)))))
       (sp '((1 ((cs2 f2 a2 ds2 c3 cs3 d3 gs3 c4 cs4 fs4 g4 gs4 a4 bf4 b4 d5 ds5
                      e5 f5 fs5 a5)))))
       (harms-list (loop repeat (length (second (first rsm-lists)))
                      collect 1))
       (set-lims-progs
        (loop for pchs in '((gs ds5) (g4 fs5) (cs4 fs5) (c4 fs5) (d3 e5) 
                            (d3 e5) (d3 b4) (cs3 b4) (cs3 ds5) (cs3 ds5) 
                            (d3 b4) (cs3 a4) (cs3 e5) (d3 e5) (d3 a5) (c4 a5) 
                            (c4 e5) (c4 d5) (c4 ds5) (c4 f5) (g4 e5) (f2 ds5) 
                            (c4 e5) (a2 f5) (cs2 e5) (cs2 fs5))
           for n from 1
           collect (list n pchs)))
       (set-lims-low (loop for s in set-lims-progs 
                        collect (first s)
                        collect (first (second s))))
       (set-lims-high (loop for s in set-lims-progs 
                         collect (first s)
                         collect (second (second s))))
       (tempus-perfectum
        (make-slippery-chicken
         '+tempus-perfectum+
         :title "Tempus Perfectum"
         :ensemble ens
         :staff-groupings '(3 3 2 3 2)
         :tempo-map '((1 (q 112)))
         :set-palette sp
         :set-map (list (list 1 harms-list))
         :set-limits-high `((all ,set-lims-high)
                            (ob (0 a5 100 a5))
                            (bn (0 g4 100 g4))
                            (hn (0 c5 100 c5))
                            (tb (0 g4 100 g4))
                            (va (0 ds5 100 ds5))
                            (vc (0 g4 100 g4)))
         :set-limits-low `((all ,set-lims-low)
                           (ob (0 g4 100 g4))
                           (hn (0 c3 100 c3))
                           (tp (0 c4 100 c4))
                           (tb (0 a2 100 a2))
                           (vn (0 d4 100 d4)))
         :rthm-seq-palette '((1 ((((3 4) - e (s) s - +q { 3 (te) - te te - })
                                  ((h.))
                                  ((h.))
                                  ((h.))
                                  ((h.))
                                  ((h.)))
                                 :pitch-seq-palette ((1 3 2 3))
                                 :marks (ppp 1 pp 2 ppp 3 cresc-beg 3 cresc-end
                                             4 )))
                             (2 ((((3 4) (h.))
                                  ((h.))
                                  (- e.. 32 - { 3 - +te te te } - - +s s - (e))
                                  (- s. 32 +e - - s s - (e) (q))
                                  ((s) - s (s) s - (q) - e e -)
                                  ((h.)))
                                 :pitch-seq-palette ((5 7 9 7 6 7 6 9 5 6 3 1
                                                        2))
                                 :marks (mf 1 s 1 2 p 3 mf 7 s 7 8 ppp 9 s 12
                                            13)))
                             (3 ((((3 4) (h.))
                                  ((q) q { 3 (ts) ts (ts) } e )
                                  (- e s s - +q q)
                                  ({ 3 (ts) - ts ts - } (e) { 3 (ts) te } 
                                     { 3 - +te ts - } q) 
                                  (h.)
                                  (+q - +e s - (s) q))
                                 :pitch-seq-palette ((10 5 7 1 3 4 10 14 8 6 7
                                                         6 10 12 7))
                                 :marks (pp 1 cresc-beg 1 cresc-end 2 f 2 p 3
                                            mf 4 dim-beg 4 dim-end 9 pp 9 mf 11
                                            dim-beg 11 dim-end 15 ppp 15
                                            cresc-beg 15 cresc-end 16 f 16 s
                                            17 mf 18 te 18)))
                             (4 ((((3 4) { 5 - fe fs fs fs - } { 3 te tq } 
                                   - +e e -)
                                  ((h.))
                                  (q (q) (e) e)
                                  ((e) e (h)) 
                                  (- s s - (e) - e (s) s - - e e -)
                                  ((e.) s +h))
                                 :pitch-seq-palette ((9 7 8 6 3 1 9 9 8 8 7 8 3
                                                        8 2 1 (9)))
                                 :marks (mf 1 dim-beg 1 s 4 ppp 6 dim-end 6 mf
                                            8 at 8 cresc-beg 8 cresc-end 11 f
                                            11 s 12 p 13 a 15 s 16 at 17 mf
                                            17)))
                             (5 ((((3 4) (e) q. - +s e. -)
                                  (e (e) (s) - e s - +e (e))
                                  (e (e) (e.) s - +e. s -)
                                  (- +s e. - (e.) s (q)) 
                                  (e (e) (h))
                                  (e (e) (h)))
                                 :pitch-seq-palette ((1 2 (5) 1 3 2 3 2 1 1 1 
                                                        (5)))
                                 :marks (fff 1 dim-beg 1 mf 3 dim-end 3 ppp 4
                                             cresc-beg 8 cresc-end 9 cresc-beg 
                                             11 cresc-end 13 f 15)))
                             (6 ((((3 4) (h.))
                                  ((e) e (h))
                                  ((h.))
                                  ((h.)) 
                                  ((h.))
                                  ((q) e (e) (q)))
                                 :pitch-seq-palette ((1 (1)))
                                 :marks (p 1 f 2)))
                             (7 ((((3 4) e (e) { 3 - ts ts ts - } (e) (q))
                                  ((h.))
                                  ((h.))
                                  ((h.)) 
                                  ((e..) 32 - e e - (q))
                                  ((h.)))
                                 :pitch-seq-palette ((2 5 4 3 1 6 7))
                                 :marks (ppp 1)))
                             (8 ((((3 4) (h.))
                                  ((h.))
                                  ((h) (e..) 32)
                                  (e (e) (h)) 
                                  ((h.))
                                  (h (q)))
                                 :pitch-seq-palette ((4 1 (2)))
                                 :marks (ppp 1 ff 3))))
         :rthm-seq-map (list (list 1 rsm-lists))
         :snd-output-dir "/tmp"
         :sndfile-palette `(((vocal-sounds
                              ((voice-womanKP-18 :frequency 1028)
                               (voice-womanKP-20 :frequency 456)
                               (voice-womanKP-21 :frequency 484)
                               (voice-womanKP-22 :frequency 591)
                               (voice-womanKP-23 :frequency 662)
                               (voice-womanKP-26 :frequency 516)
                               (voice-womanKP-29 :frequency 629)))
                             (mouth-pops-clicks
                              ((mouth_pop_2 :frequency 375)
                               (mouth_pop_2a :frequency 798)
                               (mouthnoises2 :frequency 703))))
                            ,(list sndfile-dir)
                            ("wav")))))
  (clm-play tempus-perfectum 1 '(c1) 'vocal-sounds
            :rev-amt 0.07
            :reset-snds-each-rs nil
            :pitch-synchronous t
            :src-width src-width
            :srate 44100
            :header-type clm::mus-aiff
            :data-format clm::mus-bshort
            :sndfile-extension ".aiff")
  (clm-play tempus-perfectum 1 '(c2) 'mouth-pops-clicks
            :rev-amt 0.07
            :reset-snds-each-rs nil
            :pitch-synchronous t
            :src-width src-width
            :srate 44100
            :header-type clm::mus-aiff
            :data-format clm::mus-bshort
            :sndfile-extension ".aiff")
  (setf (staff-name (get-data-data 'pl (ensemble tempus-perfectum))) " ")
  (midi-play tempus-perfectum 
             :midi-file "/tmp/tempus-perfectum.mid"
             :voices '(ob cl bn hn tp tb pr pl vn va vc))
  (cmn-display tempus-perfectum
               :file "/tmp/tempus-perfectum.eps"
               :size 11 
               :players '(ob cl bn hn tp tb pr pl vn va vc)
               :in-c t)
  (write-lp-data-for-all tempus-perfectum
                         :players '(ob cl bn hn tp tb pr pl vn va vc)
                         :in-c t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF tempus-perfectum.lsp
      
        
      The code explained
+ The concept behind the piece
Tempus perfectum was a term that designated triple meter in the mensural notation systems of the Renaissance. A triple meter based on three duple beats (tempus perfectum prolatio minor) was the equivalent of today's 3/4 time, and was indicated in the score by an empty full circle.[1]
This piece is constructed from self-similar patterns of
          eight rthm-seq objects that are all in 3/4 time. The
          specific rhythms and melodic contours of these rthm-seqs
          are loosely based on a 6-bar passage from Luciano
          Berio's Circles. In keeping with the cyclical concept and
          with the source composer, the pitch set used and the sculpting of the
          ensemble's overall tessitura are loosely based on a passage from
          Berio's Ritorno Degli Snovidenia.
The sound files chosen for the computer part are samples of female vocalise and mouth sounds (all from the Open Path Music Collection V5 and the Berklee College of Music Sampling Archive V5 sample collections at the One Laptop Per Child (OLPC) Free Sound Samples website), as a further aspect of homage to Berio's landmark work.
Notes
[1] Apel, W (1972). Harvard Dictionary of Music, 2nd Edition, 22nd Printing. Entry on Mensural Notation, p.520. Cambridge, MA: Harvard University Press
+ The opening variables
As with Nouveau Reich, the
          code for this composition begins with a number of fundamental
          variables, including num-seqs, src-width,
          and sndfile-dir, which determine the length of the
          piece, the quality of the sound file sample transposition, and the
          directory where the sound files used in the computer part are
          stored. 
The sndfile-dir variable uses Lisp's
          concatenate function together
          with slippery-chicken's built-in global variable for its
          home directory to create the string for the path to
          the sc/doc/manual/resources/ directory. 
The ensemble is also predefined here and assigned to
          the variable ens. This enables the subsequent loops that
          generate the L-system rules and the rthm-seq-map to be
          based on the number of players in the ensemble and their IDs,
          allowing the user to change the instrumentation and have the rules
          and rthm-seq-map automatically changed accordingly. 
(let* ((num-seqs 71)
       (src-width 50)
       (sndfile-dir
        (concatenate 'string
                     cl-user::+slippery-chicken-home-dir+
                     "doc/manual/resources/"))
       (ens '(((ob (oboe :midi-channel 1))
               (cl (b-flat-clarinet :midi-channel 2))
               (bn (bassoon :midi-channel 3))
               (hn (french-horn :midi-channel 4))
               (tp (c-trumpet :midi-channel 5))
               (tb (tenor-trombone :midi-channel 6))
               (pr (piano :midi-channel 7))
               (pl (piano-lh :midi-channel 8))
               (vn (violin :midi-channel 9))
               (va (viola :midi-channel 11))
               (vc (cello :midi-channel 12))
               (c1 (computer :midi-channel 13))
               (c2 (computer :midi-channel 14)))))
        
      Using L-systems to generate the rthm-seq-map
+ The elements
The order in which the rthm-seq objects occur in each
          player's part in Tempus Perfectum is determined using an
          L-system. The piece has eight separate 6-bar rthm-seqs,
          each with a numeric ID, so the list of elements in the call to
          make-l-for-lookup contains references to all 8 of these
          IDs. 
       (seqs (make-l-for-lookup 'l-seqs
                                '((1 ((1)))
                                  (2 ((2)))
                                  (3 ((3)))
                                  (4 ((4)))
                                  (5 ((5)))
                                  (6 ((6)))
                                  (7 ((7)))
                                  (8 ((8))))
                                seqs-rules))
        
      + The rules
The rules for this l-for-lookup object are defined
          immediately prior to calling make-l-for-lookup and are
          passed to the function through the
          variable seqs-rules. They are constructed using a loop
          to collect a different 4-element set from the numbers 1
          to 8 for each of the players in the piece. Having a
          separate rule for each player ensures that each player will have a
          different sequence of rthm-seqs in the course of the
          piece. 
       (seqs-rules (loop repeat (length (first ens))
                      with l = '(1 2 3 4 5 6 7 8)
                      for p from 0
                      with po = 1
                      with s = nil
                      do (setf s (list (nth (mod p 8) l)
                                       (nth (mod (+ p po) 8) l)
                                       (nth (mod (+ p (* 2 po)) 8) l)
                                       (nth (mod (+ p (* 3 po)) 8) l)))
                      collect (list (1+ p) s)
                      when (= 8 (first s))
                      do (setf po (1+ po))))
        This loop produces the following rules:
=> ((1 (1 2 3 4)) (2 (2 3 4 5)) (3 (3 4 5 6)) (4 (4 5 6 7)) (5 (5 6 7 8)) (6 (6 7 8 1)) (7 (7 8 1 2)) (8 (8 1 2 3)) (9 (1 3 5 7)) (10 (2 4 6 8)) (11 (3 5 7 1)) (12 (4 6 8 2)) (13 (5 7 1 3)))
+ Constructing the rthm-seq-map using do-simple-lookup
The inner portions of the rthm-seq-map are then
          constructed and assigned to the variable rsm-lists. They
          are assembled from the previously created l-for-lookup
          object using a loop that pairs the IDs of the players, as listed in
          the variable ens, with the flattened results of
          the do-simple-lookup method. (See the source code
          documentation for
          the flatten
          function for more detail.)
The do-simple-lookup method is called using
          the seqs variable as its first argument to create
          separate lists that are num-seqs long for each of the
          players assigned to p, including the two computer
          parts. It creates these lists by initiating each L-system iteration
          with a different axiom, consisting of consecutive numbers
          from 1 to the length of the list of players in
          the ens variable (13, one for each
          rule/player).
       (rsm-lists 
        (loop for s from 1 to (length (first ens))
           for p in (loop for i in (first ens)
                       collect (first i))
           collect (list p (flatten (do-simple-lookup seqs s num-seqs)))))
        This loop ensures that each player's part in
          the rthm-seq-map begins with a differently ordered
          sequence. After this initial difference, all subsequent sequences
          will only be created using rules 1
          through 8, as these are the only elements used in each
          rule. The combination of the rules and this particular loop produces
          a rthm-seq-map in which all players play
          all rthm-seqs of the rthm-seq-palette at
          least once during the course of the piece.
+ Self-similarity in the results
The resulting rthm-seq-map would look like this if
          written out in full, with a few of the repeating sequences
          color-coded to highlight the self-similarity:
((OB (1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 2 3 4 5 3 4 5)) (CL (2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 3 4 5 6 4 5 6)) (BN (3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 4 5 6 7 5 6 7)) (HN (4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 5 6 7 8 6 7 8)) (TP (5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 8 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 6 7 8 1 7 8 1)) (TB (6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 8 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 7 8 1 2 8 1 2)) (PR (7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 8 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 8 1 2 3 1 2 3)) (PL (8 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 1 2 3 4 2 3 4)) (VN (1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 3 4 5 6 4 5 6)) (VA (2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 4 5 6 7 5 6 7)) (VC (3 4 5 6 4 5 6 7 5 6 7 8 6 7 8 1 4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 5 6 7 8 6 7 8)) (C1 (4 5 6 7 5 6 7 8 6 7 8 1 7 8 1 2 5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 6 7 8 1 7 8 1)) (C2 (5 6 7 8 6 7 8 1 7 8 1 2 8 1 2 3 6 7 8 1 7 8 1 2 8 1 2 3 1 2 3 4 7 8 1 2 8 1 2 3 1 2 3 4 2 3 4 5 8 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 7 8 1 2 8 1 2)))
Occasional rhythmic unison in multiple parts
As can be seen in the code example above, the use of different
          axioms for each call to do-simple-lookup still produces
          a number of instances where the same sequence segment appears in more
          than one part simultaneously. This will produce a nice effect of
          coupling (non-unison rhythmic doubling) in various passages of the
          instrumental parts. 
+ Shaping the ensemble's tessitura
One set for the entire piece
This piece uses a different approach to controlling the progression
          of pitch collections (sets) over the course of a slippery
          chicken composition than that of the first three demo
          compositions. Instead of defining multiple sets and assigning their
          IDs to individual sequences in the set-map, one set of
          pitches is defined for the entire piece, and the subsets of pitches
          available to the ensemble as the piece progresses are governed using
          the set-limits-high and -low slots in
          conjunction with their all option. 
The set of pitches is given the ID 1 and assigned to
          the variable sp, and a simple loop is used to collect a
          list of 1s that is equal in length to the list
          of rthm-seqs and assigned to the
          variable harms-list. 
       (sp '((1 ((cs2 f2 a2 ds2 c3 cs3 d3 gs3 c4 cs4 fs4 g4 gs4 a4 bf4 b4 d5 ds5
                      e5 f5 fs5 a5)))))
       (harms-list (loop repeat (length (second (first rsm-lists)))
                      collect 1))
        Collecting the high and low set limits
The gradually changing upper and lower pitch limits within this set
           are then determined by first collecting a list of consecutive
           integers from 1 and pairing them with sublists
           consisting of the lowest and highest pitches available to the
           ensemble at consecutive points within the piece. The results of this
           loop are assigned to the variable set-lims-progs. 
       (set-lims-progs
        (loop for pchs in '((g4 ds5) (g4 fs5) (cs4 fs5) (c4 fs5) (d3 e5) 
                            (d3 e5) (d3 b4) (cs3 b4) (cs3 ds5) (cs3 ds5) 
                            (d3 b4) (cs3 a4) (cs3 e5) (d3 e5) (d3 a5) (c4 a5) 
                            (c4 e5) (c4 d5) (c4 ds5) (c4 f5) (g4 e5) (f2 ds5) 
                            (c4 e5) (a2 f5) (cs2 e5) (cs2 fs5))
           for n from 1
           collect (list n pchs)))
        The set-lims-progs list is then further broken down
          into the -high and -low lists with two more
          loops. The first of these collects each consecutive number and the
          first pitch of the sublist it is paired with (the low pitch), and
          the second collects the same number with the second pitch of the
          sublist (the high pitch).
The resulting lists are assigned to the
          variables set-lims-low and set-lims-high,
          which are later paired with the word all (to control the
          tessitura of the entire ensemble rather than individual instruments)
          and passed to the set-limits-high and -low
          keywords of the make-slippery-chicken function. 
       (set-lims-low (loop for s in set-lims-progs 
                        collect (first s)
                        collect (first (second s))))
       (set-lims-high (loop for s in set-lims-progs 
                         collect (first s)
                         collect (second (second s))))
        
      + The call to make-slippery-chicken
As with Nouveau Reich, this composition assigns the results
          of the make-slippery-chicken function to a variable, in
          this case tempus-perfectum. The first few keyword
          arguments of the function are either passed straightforward values
          directly (:title, :staff-groupings, and
          :tempo-map), or are passed one of the previously
          declared variables (:ensemble,
          :set-palette, and :set-map). 
       (tempus-perfectum
        (make-slippery-chicken
         '+tempus-perfectum+
         :title "Tempus Perfectum"
         :ensemble ens
         :staff-groupings '(3 3 2 3 2)
         :tempo-map '((1 (q 112)))
         :set-palette sp
         :set-map (list (list 1 harms-list))
        
      + Additional set-limits- for individual players
In addition to the overall tessitura defined above, the code
          for Tempus Perfectum also sets high and low pitch limits for
          a number of the players in the ensemble. These limits are static and
          assigned for the entire piece using x-values of 0
          and 100 for the break-point pairs. For example, the code
          specifies that the oboe will be given no pitches
          below G4, and that the bassoon, trombone, and cello will
          not have any pitches above G4. 
         :set-limits-high `((all ,set-lims-high)
                            (ob (0 a5 100 a5))
                            (bn (0 g4 100 g4))
                            (hn (0 c5 100 c5))
                            (tb (0 g4 100 g4))
                            (va (0 ds5 100 ds5))
                            (vc (0 g4 100 g4)))
         :set-limits-low `((all ,set-lims-low)
                           (ob (0 g4 100 g4))
                           (hn (0 c3 100 c3))
                           (tp (0 c4 100 c4))
                           (tb (0 a2 100 a2))
                           (vn (0 d4 100 d4)))
        
      + Multi-bar rthm-seqs with lots of rests
In defining the rthm-seq-palette, much attention is
          given in Tempus Perfectum to ensuring that there are a
          considerable number of full-bar rests in the
          multi-bar rthm-seqs. This is done to allow for more
          differentiated orchestration and more discernible combinations
          of motifs when they are superimposed.
Only one pitch-seq curve is defined for
          each rthm-seq, but the rthm-seqs in this
          piece are assigned specific marks that will be present
          in the score each time the corresponding rthm-seq
          appears. 
         :rthm-seq-palette '((1 ((((3 4) - e (s) s - +q { 3 (te) - te te - })
                                  ((h.))
                                  ((h.))
                                  ((h.))
                                  ((h.))
                                  ((h.)))
                                 :pitch-seq-palette ((1 3 2 3))
                                 :marks (ppp 1 pp 2 ppp 3 cresc-beg 3 cresc-end
                                             4 )))
                             (2 ((((3 4) (h.))
                                  ((h.))
                                  (- e.. 32 - { 3 - +te te te } - - +s s - (e))
                                  (- s. 32 +e - - s s - (e) (q))
                                  ((s) - s (s) s - (q) - e e -)
                                  ((h.)))
                                 :pitch-seq-palette ((5 7 9 7 6 7 6 9 5 6 3 1
                                                        2))
                                 :marks (mf 1 s 1 2 p 3 mf 7 s 7 8 ppp 9 s 12
                                            13)))
                             (3 ((((3 4) (h.))
                                  ((q) q { 3 (ts) ts (ts) } e )
                                  (- e s s - +q q)
                                  ({ 3 (ts) - ts ts - } (e) { 3 (ts) te } 
                                     { 3 - +te ts - } q) 
                                  (h.)
                                  (+q - +e s - (s) q))
                                 :pitch-seq-palette ((10 5 7 1 3 4 10 14 8 6 7
                                                         6 10 12 7))
                                 :marks (pp 1 cresc-beg 1 cresc-end 2 f 2 p 3
                                            mf 4 dim-beg 4 dim-end 9 pp 9 mf 11
                                            dim-beg 11 dim-end 15 ppp 15
                                            cresc-beg 15 cresc-end 16 f 16 s
                                            17 mf 18 te 18)))
                             (4 ((((3 4) { 5 - fe fs fs fs - } { 3 te tq } 
                                   - +e e -)
                                  ((h.))
                                  (q (q) (e) e)
                                  ((e) e (h)) 
                                  (- s s - (e) - e (s) s - - e e -)
                                  ((e.) s +h))
                                 :pitch-seq-palette ((9 7 8 6 3 1 9 9 8 8 7 8 3
                                                        8 2 1 (9)))
                                 :marks (mf 1 dim-beg 1 s 4 ppp 6 dim-end 6 mf
                                            8 at 8 cresc-beg 8 cresc-end 11 f
                                            11 s 12 p 13 a 15 s 16 at 17 mf
                                            17)))
                             (5 ((((3 4) (e) q. - +s e. -)
                                  (e (e) (s) - e s - +e (e))
                                  (e (e) (e.) s - +e. s -)
                                  (- +s e. - (e.) s (q)) 
                                  (e (e) (h))
                                  (e (e) (h)))
                                 :pitch-seq-palette ((1 2 (5) 1 3 2 3 2 1 1 1 
                                                        (5)))
                                 :marks (fff 1 dim-beg 1 mf 3 dim-end 3 ppp 4
                                             cresc-beg 8 cresc-end 9 cresc-beg 
                                             11 cresc-end 13 f 15)))
                             (6 ((((3 4) (h.))
                                  ((e) e (h))
                                  ((h.))
                                  ((h.)) 
                                  ((h.))
                                  ((q) e (e) (q)))
                                 :pitch-seq-palette ((1 (1)))
                                 :marks (p 1 f 2)))
                             (7 ((((3 4) e (e) { 3 - ts ts ts - } (e) (q))
                                  ((h.))
                                  ((h.))
                                  ((h.)) 
                                  ((e..) 32 - e e - (q))
                                  ((h.)))
                                 :pitch-seq-palette ((2 5 4 3 1 6 7))
                                 :marks (ppp 1)))
                             (8 ((((3 4) (h.))
                                  ((h.))
                                  ((h) (e..) 32)
                                  (e (e) (h)) 
                                  ((h.))
                                  (h (q)))
                                 :pitch-seq-palette ((4 1 (2)))
                                 :marks (ppp 1 ff 3))))
        A cmn-display of the rthm-seq-palette
A cmn-display processing of the above palette produces
          the following graphic:
|  | 
Incorporating CLM and sound files into the composition
+ Adding separate computer players to the ensemble
In order to generate rhythmically and melodically independent
          computer parts for the piece, two separate computer players are added
          to the ensemble. These are given the IDs c1
          and c2, and assigned the computer
          instrument from
          the +slippery-chicken-standard-instrument-palette:
            '(((ob (oboe :midi-channel 1))
               (cl (b-flat-clarinet :midi-channel 2))
               (bn (bassoon :midi-channel 3))
               (hn (french-horn :midi-channel 4))
               (tp (c-trumpet :midi-channel 5))
               (tb (tenor-trombone :midi-channel 6))
               (pr (piano :midi-channel 7))
               (pl (piano-lh :midi-channel 8))
               (vn (violin :midi-channel 9))
               (va (viola :midi-channel 11))
               (vc (cello :midi-channel 12))
               (c1 (computer :midi-channel 13))
               (c2 (computer :midi-channel 14))))
        These two parts are also included in the loop that generates
          the rthm-seq-map, as described above.
NB: The phrase "computer player" used here does not mean
	  a live performer sitting at a computer and playing it in
	  real-time. Instead, it refers to a player object within
	  the ensemble of the slippery-chicken object
	  that is assigned to the computer instrument of
	  the slippery-chicken-standard-instrument-palette. Each
	  of the two players using the computer
	  instrument in this piece will merely be separate parts (separate
	  rhythmic and pitch structures) that will eventually be used to create
	  fixed-media (non-real-time) "tape" parts, which will most likely be
	  played back using audio software on a computer.
+ Setting up the output directory and sndfile-palette
Next, the output directory for the CLM output and the source sound
          files used to generate that output are specified using
          the :snd-output-dir and :sndfile-palette
          keywords of the make-slippery-chicken
          function.
The sndfile-palette uses the previously
          defined sndfile-dir variable to specify the path to the
          location of the source sound files. Since all of the files
          are .wav files, the string "wav" is added
          to the extensions slot of
          the sndfile-palette, with the extension omitted from the
          file names. 
       (sndfile-dir
        (concatenate 'string
                     cl-user::+slippery-chicken-home-dir+
                     "doc/manual/resources/"))
       [...]
         :snd-output-dir "/tmp"
         :sndfile-palette `(((vocal-sounds
                              ((voice-womanKP-18 :frequency 1028)
                               (voice-womanKP-20 :frequency 456)
                               (voice-womanKP-21 :frequency 484)
                               (voice-womanKP-22 :frequency 591)
                               (voice-womanKP-23 :frequency 662)
                               (voice-womanKP-26 :frequency 516)
                               (voice-womanKP-29 :frequency 629)))
                             (mouth-pops-clicks
                              ((mouth_pop_2 :frequency 375)
                               (mouth_pop_2a :frequency 798)
                               (mouthnoises2 :frequency 703))))
                            ,(list sndfile-dir)
                            ("wav")))))
        One group for each call to clm-play
The sndfile-palette is constructed to consist of two
          groups of source sound files, the first being vocalized sounds, the
          second being non-vocalized mouth sounds. These groups are given the
          names vocal-sounds and mouth-pops-clicks,
          which will be used in the two calls to clm-play
          below.
Preparing pitch-synchronous transposition of the source files
Fundamental frequencies are specified for each of the sound files,
          as the calls to clm-play will employ
          the pitch-synchronous option. 
+ Generating the computer parts
The two computer parts are then produced by using separate calls
          to clm-play
          for each sound file group and each computer player, specifying values
          for a number of the method's keyword arguments. These calls also make
          use of the previously defined variable src-width, which
          is set to 50 to produce a slightly higher quality of
          sample interpolation when transposing the sound files.
       (src-width 50)
       [...]
  (clm-play tempus-perfectum 1 '(c1) 'vocal-sounds
            :rev-amt 0.07
            :reset-snds-each-rs nil
            :pitch-synchronous t
            :src-width src-width
            :srate 44100
            :header-type clm::mus-aiff
            :data-format clm::mus-bshort
            :sndfile-extension ".aiff")
  (clm-play tempus-perfectum 1 '(c2) 'mouth-pops-clicks
            :rev-amt 0.07
            :reset-snds-each-rs nil
            :pitch-synchronous t
            :src-width src-width
            :srate 44100
            :header-type clm::mus-aiff
            :data-format clm::mus-bshort
            :sndfile-extension ".aiff")
        Required arguments
The first required argument to clm-play is
          the slippery-chicken object itself, and the second is
          the section within the piece from which to start. Since
          Tempus Perfectum has only one section, the
          number 1 is specified here. The third required argument
          is the ID of the player (or players) whose part is to serve as the
          basis for the resulting CLM output. This is specified
          as c1 in the first case and c2 in the
          second. The final required argument is the ID of the group of sound
          files specified in the sndfile-palette from which the
          CLM output is to be generated. The c1 part is to be
          based on the vocal-sounds group, and the second on
          the mouth-pops-clicks group.
Optional keyword arguments
Each of the calls to clm-play uses the same optional
          arguments. The same amount of reverb is specified for each output
          file using the rev-amt keyword. Setting
          the reset-snds-each-rs argument to NIL
          causes the method to cycle through the sound files of the given group
          for the whole duration of the piece, without beginning at the head of
          the group with each new rthm-seq. 
Setting the pitch-synchronous argument
          to T causes the method to transpose the source sound
          files to match the specific pitches of the part on which the output
          is based, using the frequency value specified in
          the sndfile-palette as the basis for this
          transposition. The src-width argument determines the
          quality of the interpolation during transposition, as mentioned
          above. 
The last four arguments determine the output format of the files
          that CLM will generate. The srate
          and sndfile-extension arguments determine the sample
          rate and file suffix for the files
          produced. The header-type and data-format
          arguments are CLM arguments and must be preceded by
          the clm:: package qualifier. The values given here will
          result in stereo .aiff audio files, with the
          extension .aiff (default is
          otherwise .aif), and with a format of 16-bit integer
          (big endian). More on these arguments can be found on
          the slippery chicken and CLM page of
          the manual.
+ Generating the MIDI and printable score output
Changing the staff name of the piano left-hand
Before generating the printable score output, one last change is
          made to the slippery-chicken
          object. The piano-lh instrument object of
          the +slippery-chicken-standard-instrument-palette+ has
          no entry for the staff-name
          and staff-short-name slots, meaning that those slots
          contain NIL. LilyPond handles this by printing blank
          space for the corresponding staff names in the score. CMN, however,
          will print NIL into the score. 
To work around this, the code for Tempus Perfectum sets
          the staff-name slot of the
          corresponding instrument object stored within
          the ensemble slot of the slippery-chicken
          object itself, rather than changing the value in
          the instrument-palette. The setf approach
          to changing slot values is used here rather than
          the set-slot method, though that method could be used as
          well (see the page
          on instruments and
          instrument-palettes for more detail.)
(setf (staff-name (get-data-data 'pl (ensemble tempus-perfectum))) " ")
Excluding the computer parts from MIDI and score output
The calls to midi-play, cmn-display,
          and write-lp-data-for-all then use
          the :voices and :player keywords to exclude
          the computer players from the MIDI and printable score output. This
          is done by specifying in list form the IDs of all players that are to
          be included in the output. Additionally, the :in-c
          argument is set to T to produce C-scores, and
          the :size argument of clm-play is set
          to 11 to ensure that the full score fits on the
          page.
  (midi-play tempus-perfectum 
             :midi-file "/tmp/tempus-perfectum.mid"
             :voices '(ob cl bn hn tp tb pr pl vn va vc))
  (cmn-display tempus-perfectum
               :file "/tmp/tempus-perfectum.eps"
               :size 11 
               :players '(ob cl bn hn tp tb pr pl vn va vc)
               :in-c t)
  (write-lp-data-for-all tempus-perfectum
                         :players '(ob cl bn hn tp tb pr pl vn va vc)
                         :in-c t))
      Adding the \midi { } command to the LilyPond score file
A final step was undertaken to produce the MP3 of the MIDI mock-up
        for this tutorial. The midi-play method of
        the slippery-chicken class only reflects static dynamics
        in the score, without implementing crescendos/diminuendos or
        articulation. The LilyPond application is capable of producing a MIDI
        file that reflects articulation and gradual dynamic changes slightly
        more accurately (though the authors of that program too explicitly
        state that LilyPond is not made for MIDI).
In order to have LilyPond produce a second MIDI file, the
        resulting _tempus-perfectum-score.ly file produced
        by write-lp-data-for-all was edited prior to having
        LilyPond render the PDF of the score, by inserting one simple line,
        namely \midi { }, as seen below. 
The MIDI file produced by LilyPond
        (_tempus-perfectum-score.midi) will have separate tracks
        for each player, but all program numbers (patches) will be set
        to 1 (Acoustic Grand Piano) by default. This was not an
        issue when producing the MIDI mock-up for Tempus Perfectum, as
        the resulting MIDI file was then imported into a MIDI sequencer and the
        tracks were manually assigned to instrument tracks that used
        sample-based VST plug-ins.
\version "2.14.2"
\include "tempus-perfectum-def.ly"
\header {
  title ="Tempus Perfectum" 
  tagline = ##f
  composer = ##f
}
\score {
  \keepWithTag #'score \music
  \midi { }
  \layout { }
}