Tonal composition with slippery chicken

+ Associated example files

close


slippery chicken was not explicitly designed to produce tonal music. However, with a bit of planning and some careful programming, the software can be used to algorithmically generate tonal compositions as well.

The approach used in this demo composition defines 17 fixed, polyphonic fragments that can be transposed, interchanged, and interconnected in a kind of modular composition technique. These modules are then assembled algorithmically using a repeatable pseudo-random function together with the get-linear-sequence method of the l-for-lookup class.

+ The modules: 3- and 4-voiced improvisation patterns

The modules that serve as the building blocks for this piece are based on an approach to classical keyboard improvisation and voice-oriented (rather than harmony-based) musical analysis taught to by Volkhardt Preuss at the Hochschule für Musik und Theater in Hamburg, Germany.

This method collects fundamental musical structures into five basic categories: Fauxbourdon (parallel triads in first inversion, often with 7-6 suspensions), Ruggiero (Dominant-Tonic cadences), Sekundakkord (Dominant-seventh chords with the 7 in the bass), Orgelpunkt (pedal-point sequences over a sustained bass), and Terzfall (falling third sequences). The first four of these have been implemented in this piece.

In this categorical approach, the five pattern types are first reduced and presented as core sequences, in which all of the voices progress in simultaneous half-note motion. This half-note basis can then be extended and elaborated (possibly over several measures) to create more fluid music. The demo composition in this tutorial sticks to just the short, core progressions with half-note motion.


core-fauxbourdon
A core fauxbourdon with no suspensions
expanded-fauxbourdon
An elaborated version of the same fauxbourdon with octave-displaced voice-crossings (from Bach's WTK I, Prelude 2)

close

+ sc's pitch-selection algorithm and voice-leading

slippery chicken's automatic pitch-selection algorithm was designed to produce atonal music, and it needs the use of some creative constraints to get it to produce tonal melodies with traditional voice-leading.

Because slippery chicken uses the same pitch set for all players in any given sequence, the approach taken here is to confine each set to different subsets for each player in each sequence using the set-limits-high and set-limits-low slots, thereby enabling an exact mapping of the numbers in each pitch-seq to specific pitches of the current set.

The code for this composition applies this technique once for each rthm-seq, and then transposes the pitch sets using the transposition slot of the set-palette object, thereby ensuring that the same scale degrees are selected for the pitches of each rthm-seq when that rthm-seq is repeated in conjunction with different sets.

close

+ Converting note-names to pitch-seq numbers

A small function has been written for this piece that makes it easier to determine the numbers required for the tonal pitch-seqs. It takes as its two arguments a list of note-name symbols that are to be converted into pitch-seq numbers, and the "source" list of note-name symbols to which the pitch-seq numbers are to be applied.

This function allows the user to enter note-name symbols directly, which are then automatically converted into pitch-seq numbers, rather than counting through all the pitches in the set or subset to determine which numbers are needed in order to produce them. The function requires that the :avoid-melodic-octaves and :avoid-used-notes arguments of the make-slippery-chicken function are both set to NIL in order to work properly.

Every pitch in the first list must be present in the "source" list (second argument) to prevent errors. Enharmonically equivalent pitches are not considered equal; the note-name symbols must be exact (i.e. GS4 must be written as GS4 and not AF4). The function then determines the resulting pitch-seq numbers by returning the position (index number) of each specified pitch in the source set and subtracting the required offset so that the lowest pitch is always 1.

(defun pitches-to-pseq (pitch-list set)
  (let* (positions lowest pseq)
    (setf positions (mapcar #'(lambda (x) (position x set)) pitch-list))
    (setf lowest (1- (first (sort (copy-list positions) #'<))))
    (setf pseq (mapcar #'(lambda (x) (- x lowest)) positions))
    pseq))
SC> (pitches-to-pseq '(c4 e4 g4 f4 d4 e4 c4) '(a3 b3 c4 d4 e4 f4 g4 a4))
(1 3 5 4 2 3 1)

close

+ Generating the set-palette with :transposition

Core sets

The code for this piece uses two primary sets: one that is predominantly major, with chromatic passing pitches, and one that is predominantly minor, also with chromatic passing pitches. These are defined at the top of the variable declarations within the let* enclosure. Since the pitches-to-pseq function requires exact note-name symbols, the sets contain both enharmonic spellings of some of the pitches, such as fs4 and gf4.

       (core-set-maj '(g2 a2 bf2 b2 c3 d3 ef3 e3 f3 g3 a3 b3 c4 cs4 d4 ef4 e4
                       f4 fs4 gf4 g4 gs4 af4 a4 bf4 b4 c5 d5 e5 f5 fs5 g5 b5
                       c6))
       (core-set-min '(g2 af2 bf2 c3 ef3 f3 fs3 g3 af3 bf3 b3 c4 d4 ef4 e4 f4
                       fs4 gf4 g4 af4 a4 bf4 b4 c5 ef5 e5 f5))

Using a loop to create the set-palette

The following loop is used to create the set-palette and assign it to the variable sp:

       (sp 
        (loop for i from 0 to 11
           if (= i 0)
           collect (list "0-MAJ" (list core-set-maj))
           and collect (list "0-MIN" (list core-set-min))
           else 
           collect (list (format nil "~a-MAJ" i)
                         (list "0-MAJ" ':transposition i))
           and collect (list (format nil "~a-MIN" i)
                             (list "0-MIN" ':transposition i))))

The :transposition slot

The set-palette is assembled automatically in the loop by collecting 12 transpositions (from 0 to 11 semitones) of each of the core-set- variables declared above. The integer value passed to the :transposition slot of a set within a set-palette object will automatically transpose the source set by that many semitones.

Assembling the set-palette in this way means there will be one major and one minor set for each of the 12 chromatic pitch-classes (0 = C, 1 = C-sharp, 2 = D, etc.). Modulating to a different key within the piece, then, is made possible by selecting the corresponding set in the set-map.

Using strings as IDs

The IDs for the individual sets of this set-palette are strings, such as "0-MAJ" or "0-MIN", rather than the numbers or symbols used in the other tutorials. This allows for easier concatenation of numbers and symbols when automatically assembling the set-palette within the loop macro.

The same number used for the semitone transposition is combined with either "-MAJ" or "-MIN" to create the ID for the given set.

The resulting set-palette would thus look like this, if written out in full:

(("0-MAJ"
  ((G2 A2 BF2 B2 C3 D3 EF3 E3 F3 G3 A3 B3 C4 CS4 D4 EF4 E4 F4 FS4 GF4 G4
    GS4 AF4 A4 BF4 B4 C5 D5 E5 F5 FS5 G5 B5 C6)))
 ("0-MIN"
  ((G2 AF2 BF2 C3 EF3 F3 FS3 G3 AF3 BF3 B3 C4 D4 EF4 E4 F4 FS4 GF4 G4 AF4
    A4 BF4 B4 C5 EF5 E5 F5)))
 ("1-MAJ" ("0-MAJ" :TRANSPOSITION 1)) 
 ("1-MIN" ("0-MIN" :TRANSPOSITION 1))
 ("2-MAJ" ("0-MAJ" :TRANSPOSITION 2)) 
 ("2-MIN" ("0-MIN" :TRANSPOSITION 2))
 ("3-MAJ" ("0-MAJ" :TRANSPOSITION 3)) 
 ("3-MIN" ("0-MIN" :TRANSPOSITION 3))
 ("4-MAJ" ("0-MAJ" :TRANSPOSITION 4)) 
 ("4-MIN" ("0-MIN" :TRANSPOSITION 4))
 ("5-MAJ" ("0-MAJ" :TRANSPOSITION 5)) 
 ("5-MIN" ("0-MIN" :TRANSPOSITION 5))
 ("6-MAJ" ("0-MAJ" :TRANSPOSITION 6)) 
 ("6-MIN" ("0-MIN" :TRANSPOSITION 6))
 ("7-MAJ" ("0-MAJ" :TRANSPOSITION 7)) 
 ("7-MIN" ("0-MIN" :TRANSPOSITION 7))
 ("8-MAJ" ("0-MAJ" :TRANSPOSITION 8)) 
 ("8-MIN" ("0-MIN" :TRANSPOSITION 8))
 ("9-MAJ" ("0-MAJ" :TRANSPOSITION 9)) 
 ("9-MIN" ("0-MIN" :TRANSPOSITION 9))
 ("10-MAJ" ("0-MAJ" :TRANSPOSITION 10))
 ("10-MIN" ("0-MIN" :TRANSPOSITION 10))
 ("11-MAJ" ("0-MAJ" :TRANSPOSITION 11))
 ("11-MIN" ("0-MIN" :TRANSPOSITION 11)))

close

+ Using the l-for-lookup class for harmonic progressions

The l-for-lookup class has a method called get-linear-sequence which lends itself to the generation of harmonic progressions. Instead of using the rules of a standard l-for-lookup (Lindenmayer System) object to create its resulting list by substitution (or rewriting), it uses them in rotation.

Each time a particular rule-data pair is accessed, the method selects the next item in the data list associated with the given rule. When it has used the last item in a data list, it cycles around and begins at the top of the list the next time that rule-data pair is accessed.

The l-for-lookup object and call to get-linear-sequence in this simple example here illustrate this method:

(let ((lfl (make-l-for-lookup 'lfl
                              nil
                              '((1 (2 3))
                                (2 (1))
                                (3 (1 2))))))
  (get-linear-sequence lfl 1 30))

=> (1 2 1 3 1 2 1 3 2 1 2 1 3 1 2 1 3 2 1 2 1 3 1 2 1 3 2 1 2 1)

The data slot of the l-for-lookup object can be set to NIL when used with get-linear-sequence, since this method does not use that slot. The call to get-linear-sequence states the name of the object to access, the first ID to use as a look-up key (a.k.a. the "seed" or "axiom"; 1 in this example), and the length of the list to return (30 here).

The method then gets the first item in the rules list associated with the seed argument, which in this example is 2, and uses that item as the look-up ID to get the next item. The only item in the list associated with rule 2 in this example is 1. The number 1 is then used again here as the look-up ID, but this time it gets the next item from the associated data list: 3. Now that all items associated with rule 1 have been used, the next time 1 is used as a look-up ID, it will select the first item of the list again, namely 2, and so on.

The procedure for this piece

The procedure used to generate the sequence of modules in this piece (i.e. the rthm-seq-map) function similarly. Instead of using numbers, however, the keys consist of symbols that identify the different module types, and the associated data consist of symbols that identify the module types to which each given module type may progress. The key-data items of this initial object, and the resulting list, are used later as keys again to look up data from two further association lists, namely transposition-rules-al and combos-al.

The symbol names for each of the module types here are created from their kind, their variation, and their modality (major, minor, starting major but ending minor, etc.). They each represent groups of modules, as defined in the combos-al variable below. Each group may have one or more possible combination of voices (e.g., the cantizans may appear in the soprano voice in one combination, but in the alto voice in another, etc.)

       (seq-rules 
        (make-l-for-lookup 'seq-rules
                           nil
                           '((fauxbourdon-a-maj 
                              (ruggiero-a-maj ruggiero-b-maj
                                ruggiero-b-maj-to-min ruggiero-c-maj))
                             (fauxbourdon-a-min 
                              (ruggiero-a-min ruggiero-d-min-to-maj))
                             (ruggiero-a-maj 
                              (fauxbourdon-a-maj sekund-a-maj ped-a-maj))
                             (ruggiero-a-min 
                              (fauxbourdon-a-min sekund-a-min))
                             (ruggiero-b-maj 
                              (fauxbourdon-a-maj sekund-a-maj ped-a-maj))
                             (ruggiero-b-maj-to-min 
                              (fauxbourdon-a-min sekund-a-min))
                             (ruggiero-c-maj 
                              (fauxbourdon-a-maj sekund-a-maj ped-a-maj))
                             (ruggiero-d-min-to-maj 
                              (fauxbourdon-a-maj sekund-a-maj ped-a-maj))
                             (ruggiero-e-maj 
                              (fauxbourdon-a-maj sekund-a-maj ped-a-maj))
                             (sekund-a-maj 
                              (ruggiero-e-maj))
                             (sekund-a-min 
                              (ruggiero-a-min ruggiero-d-min-to-maj))
                             (ped-a-maj 
                              (sekund-a-maj)))))

Getting the linear sequence

The sequence of modules is then created by calling the get-linear-sequence method with the symbol fauxbourdon-a-maj as the seed, and a list length of num-seqs, which is the first variable declared in this let* enclosure (29 here). The resulting list is stored in the variable seq-seq.

       (seq-seq (get-linear-sequence seq-rules 'fauxbourdon-a-maj num-seqs))

The resulting sequence of modules in this instance is:

(FAUXBOURDON-A-MAJ RUGGIERO-A-MAJ FAUXBOURDON-A-MAJ RUGGIERO-B-MAJ
 FAUXBOURDON-A-MAJ RUGGIERO-B-MAJ-TO-MIN FAUXBOURDON-A-MIN RUGGIERO-A-MIN
 FAUXBOURDON-A-MIN RUGGIERO-D-MIN-TO-MAJ FAUXBOURDON-A-MAJ RUGGIERO-C-MAJ
 FAUXBOURDON-A-MAJ RUGGIERO-A-MAJ SEKUND-A-MAJ RUGGIERO-E-MAJ
 FAUXBOURDON-A-MAJ RUGGIERO-B-MAJ SEKUND-A-MAJ RUGGIERO-E-MAJ SEKUND-A-MAJ
 RUGGIERO-E-MAJ PED-A-MAJ SEKUND-A-MAJ RUGGIERO-E-MAJ FAUXBOURDON-A-MAJ
 RUGGIERO-B-MAJ-TO-MIN SEKUND-A-MIN RUGGIERO-A-MIN)

Adding a coda

This particular sequence, created using a list length of 29, generates a sequence that ends with a V-I cadence (a ruggiero module). However, not all list lengths will generate lists with a ruggiero as their last element; a length of 30, for example, would generate a sequence that ends with SEKUND-A-MIN, and the piece would sound like it gets cut off in the middle of a phrase.

To remedy this, a small loop is used to generate a coda if necessary. The loop looks at the last item in the seq-seq list, and if it does not start with the characters RUG, it collects a new linear sequence of module symbol names, using the same rules as above, until a ruggiero is reached. It saves this new short list to the variable coda, which is then appended to the end of the seq-seq. The sequence is now complete and will end with a V-I cadence.

       (coda
        (loop
           with l = (first (last seq-seq))
           until (equalp (subseq (format nil "~a" l) 0 3)
                         "RUG")
           do
             (setf l (first (last (get-linear-sequence seq-rules l 2))))
           collect l))
       (seq-seq (append seq-seq coda))

close

+ An assoc-list object to store transposition rules

A number of the modules selected for this particular slippery-chicken object modulate to different keys and different modes (major or minor). The ruggiero-b-maj-to-min module, for example, starts on the V (Dominant) of a given major key and uses a musical sequence to modulate to the minor key three half-steps higher (e.g. from C major to E-flat minor).

Each consecutive set chosen for the set-map is therefore dependent on whether the previous module modulates or not. Accordingly, then, a list of transposition rules are first collected into an association list (an assoc-list object), and each of the symbol names defined for each module in the l-for-lookup object above are once again used as keys to store lists of two items: a positive or negative integer to determine the number of half-steps by which the piece is to modulate at the end of each module, and a symbol to indicate whether the new key is to be major or minor. This assoc-list object is stored in the variable transposition-rules-al.

       (transposition-rules-al 
        (make-assoc-list 'transposition-rules
                         '((fauxbourdon-a-maj (0 maj))
                           (fauxbourdon-a-min (0 min))
                           (ruggiero-a-maj (0 maj))
                           (ruggiero-a-min (0 min))
                           (ruggiero-b-maj (3 maj))
                           (ruggiero-b-maj-to-min (3 min))
                           (ruggiero-c-maj (-3 maj))
                           (ruggiero-d-min-to-maj (0 maj))
                           (ruggiero-e-maj (0 maj))
                           (sekund-a-maj (0 maj))
                           (sekund-a-min (0 min))
                           (ped-a-maj (0 maj)))))

A small loop is then used to collect the key-change progression by calculating the transpositions associated with each of the modules listed in the seq-seq variable created above. To this end, each item in the seq-seq is consecutively used as the key to perform a look-up into the transposition-rules-al association list.

       (transpositions-changes 
        (let ((n 0)
              rule)
          (loop for seq in (subseq seq-seq 0 (1- (length seq-seq)))
             do (setf rule (get-data-data seq transposition-rules-al))
             collect (list
                      (setf n (mod (+ n (first rule)) 12))
                      (second rule))))) 

The resulting list, which is stored in the variable transpositions-changes, consists of a series of two-item lists, each of which contain the number and symbol (MAJ or MIN) required to create the string ID of one of the sets in the set-palette. The loop therefore performs a cumulative summation of the corresponding transposition integers from the transposition-rules-al in modulo 12 to ensure that the transposition always returns a number between 0 and 11, the first part of an existing set ID. The results for this particular piece (with 29 initial sequences) is:

((0 MAJ) (0 MAJ) (0 MAJ) (0 MAJ) (3 MAJ) (3 MAJ) (6 MIN) (6 MIN) (6 MIN)
 (6 MIN) (6 MAJ) (6 MAJ) (3 MAJ) (3 MAJ) (3 MAJ) (3 MAJ) (3 MAJ) (3 MAJ)
 (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ) (6 MAJ)
 (9 MIN) (9 MIN))

However, this list is just a list of the key changes that occur at the end of each module. In order to have one key for each module, the next variable, transpositions, pushes (0 MAJ) to the head of this list. There is now one key and one mode stored for each item in the seq-seq

       (transpositions (push '(0 MAJ) transpositions-changes))

close

+ Assembling the set-map from the transpositions list

The set-map is then created from the transpositions variable. A small loop collects a list of strings constructed by concatenating the integer from each two-item list with the corresponding symbol.

       (sm 
        (loop for trnsp in transpositions
           collect (format nil "~a-~a" (first trnsp) (second trnsp))))

Each of these strings will be equal to the ID of one of the transposed sets in the set-palette. The resulting list is stored in the variable sm, which is passed directly to the set-map slot of the slippery-chicken object made below. The set-map for this particular sequence of 29 modules looks like this:

("0-MAJ" "0-MAJ" "0-MAJ" "0-MAJ" "3-MAJ" "3-MAJ" "6-MIN" "6-MIN" "6-MIN"
 "6-MIN" "6-MAJ" "6-MAJ" "3-MAJ" "3-MAJ" "3-MAJ" "3-MAJ" "3-MAJ" "3-MAJ"
 "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ" "6-MAJ"
 "9-MIN" "9-MIN")

close

+ An assoc-list object to store module combinations

As mentioned above, each module type named in the l-for-lookup object and the subsequent seq-seq variable represents a group of possible voice combinations that have the same pitch, rhythm, and harmonic structure. These combinations are defined in another assoc-list object here and assigned to the variable combos-al:

       (combos-al
        (make-assoc-list 
         'combos
         '((fauxbourdon-a-maj ((fx-1-maj-c fx-1-maj-a fx-1-maj-t 2-rest)
                               (fx-1-maj-c fx-1-maj-a 2-rest fx-2-maj-t)
                               (fx-3-maj-c fx-1-maj-c 2-rest fx-2-maj-t)))
           (fauxbourdon-a-min ((fx-1-min-c fx-1-min-a fx-1-min-t 2-rest)
                               (fx-1-min-c fx-1-min-a 2-rest fx-2-min-t)))
           (ruggiero-a-maj ((rg-1-maj-c rg-1-maj-a rg-1-maj-t 2-rest)))
           (ruggiero-a-min ((rg-1-min-c rg-1-min-a rg-1-min-t 2-rest)
                            (rg-4-min-c rg-4-min-a 2-rest rg-4-min-b)))
           (ruggiero-b-maj ((rg-2-maj-c rg-2-maj-a 5-rest rg-2-maj-t)))
           (ruggiero-b-maj-to-min ((rg-2-min-c rg-2-min-a 5-rest rg-2-min-t)))  
           (ruggiero-c-maj ((rg-3-maj-c rg-3-maj-a rg-3-maj-t rg-3-maj-b)))
           (ruggiero-d-min-to-maj ((rg-5-min-c rg-5-min-a rg-5-min-t
                                               rg-5-min-b)))
           (ruggiero-e-maj ((rg-6-maj-c rg-6-maj-a 2-rest rg-6-maj-b)))
           (sekund-a-maj ((sek-1-maj-c sek-1-maj-a sek-1-maj-t sek-1-maj-b)
                          (sek-2-maj-c sek-2-maj-a sek-2-maj-t sek-2-maj-b)))
           (sekund-a-min ((sek-3-min-c sek-3-min-a sek-3-min-t sek-3-min-b)))
           (ped-a-maj ((ped-1-maj-c ped-1-maj-a ped-1-maj-t ped-1-maj-b)))))) 

Each key in this association list is exactly the same as the keys in the l-for-lookup object and the transposition-rules-al. The data lists associated with each key consist of the IDs of rthm-seq objects defined in the rthm-seq-palette below.

Each rthm-seq below consists of one voice within one of the modules. Each data item in the combos-al is therefore a set of four rthm-seq IDs, one for each player. These are first collected here in combination to make the identification easier for the programmer. They will then be disassembled and reassembled into the rthm-seq-map in the next two steps below.

close

+ Assembling the rthm-seq-map from the combos-al

Collecting the random combos first

A small loop is first used to collect the list of rthm-seq combinations for the subsequent rthm-seq-map. It passes through the list of module types stored in the seq-seq variable and uses each one as a look-up key into the combos-al. It uses slippery chicken's own random-rep function to select a random voice combo from the data list associated with each module type.

       (rsm-combos 
        (let* (c)
          (random-rep 1 t) ; reset random state each time for same results
          (loop for s in seq-seq
             do (setf c (get-data-data s combos-al))
             collect
               (nth (random-rep (length c)) c))))

The random-rep function has an optional argument that allows the user to reset the current Lisp's random state, allowing for the exact same list of randomly selected items to be returned each time. In this example, random-rep is called first, prior to the loop, so that the random state is reset. The result of this loop is stored in the rsm-combos variable and produces the following list for this example:

Update 19.6.13: The random-rep function has been replaced with a hard-coded list in tonal-algo-example.lsp so that the enharmonic corrections done 'by hand' still function when the file is run in any version of Lisp.

((FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-1-MAJ-C RG-1-MAJ-A RG-1-MAJ-T 2-REST)
 (FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-2-MAJ-C RG-2-MAJ-A 5-REST RG-2-MAJ-T)
 (FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-2-MIN-C RG-2-MIN-A 5-REST RG-2-MIN-T)
 (FX-1-MIN-C FX-1-MIN-A 2-REST FX-2-MIN-T)
 (RG-4-MIN-C RG-4-MIN-A 2-REST RG-4-MIN-B)
 (FX-1-MIN-C FX-1-MIN-A 2-REST FX-2-MIN-T)
 (RG-5-MIN-C RG-5-MIN-A RG-5-MIN-T RG-5-MIN-B)
 (FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-3-MAJ-C RG-3-MAJ-A RG-3-MAJ-T RG-3-MAJ-B)
 (FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-1-MAJ-C RG-1-MAJ-A RG-1-MAJ-T 2-REST)
 (SEK-2-MAJ-C SEK-2-MAJ-A SEK-2-MAJ-T SEK-2-MAJ-B)
 (RG-6-MAJ-C RG-6-MAJ-A 2-REST RG-6-MAJ-B)
 (FX-3-MAJ-C FX-1-MAJ-C 2-REST FX-2-MAJ-T)
 (RG-2-MAJ-C RG-2-MAJ-A 5-REST RG-2-MAJ-T)
 (SEK-2-MAJ-C SEK-2-MAJ-A SEK-2-MAJ-T SEK-2-MAJ-B)
 (RG-6-MAJ-C RG-6-MAJ-A 2-REST RG-6-MAJ-B)
 (SEK-1-MAJ-C SEK-1-MAJ-A SEK-1-MAJ-T SEK-1-MAJ-B)
 (RG-6-MAJ-C RG-6-MAJ-A 2-REST RG-6-MAJ-B)
 (PED-1-MAJ-C PED-1-MAJ-A PED-1-MAJ-T PED-1-MAJ-B)
 (SEK-2-MAJ-C SEK-2-MAJ-A SEK-2-MAJ-T SEK-2-MAJ-B)
 (RG-6-MAJ-C RG-6-MAJ-A 2-REST RG-6-MAJ-B)
 (FX-1-MAJ-C FX-1-MAJ-A FX-1-MAJ-T 2-REST)
 (RG-2-MIN-C RG-2-MIN-A 5-REST RG-2-MIN-T)
 (SEK-3-MIN-C SEK-3-MIN-A SEK-3-MIN-T SEK-3-MIN-B)
 (RG-1-MIN-C RG-1-MIN-A RG-1-MIN-T 2-REST))

Separating the list of combos into a list for the rthm-seq-map

The next little loop then parses the rsm-combos list into separate lists for each player, with the syntax necessary to make a rthm-seq-map object, and stores the resulting list in the variable rsm. This variable will then be used directly within the call to make-slippery-chicken.

       (rsm 
        (loop for p in '(vo vt va vc)
           for n from 0
           collect
             (list p 
                   (flatten
                    (loop for c in rsm-combos
                       collect 
                         (list (nth n c)))))))

The result of this loop for this example is as follows:

((VO
  (FX-1-MAJ-C RG-1-MAJ-C FX-1-MAJ-C RG-2-MAJ-C FX-1-MAJ-C RG-2-MIN-C
   FX-1-MIN-C RG-4-MIN-C FX-1-MIN-C RG-5-MIN-C FX-1-MAJ-C RG-3-MAJ-C
   FX-1-MAJ-C RG-1-MAJ-C SEK-2-MAJ-C RG-6-MAJ-C FX-3-MAJ-C RG-2-MAJ-C
   SEK-2-MAJ-C RG-6-MAJ-C SEK-1-MAJ-C RG-6-MAJ-C PED-1-MAJ-C SEK-2-MAJ-C
   RG-6-MAJ-C FX-1-MAJ-C RG-2-MIN-C SEK-3-MIN-C RG-1-MIN-C))
 (VT
  (FX-1-MAJ-A RG-1-MAJ-A FX-1-MAJ-A RG-2-MAJ-A FX-1-MAJ-A RG-2-MIN-A
   FX-1-MIN-A RG-4-MIN-A FX-1-MIN-A RG-5-MIN-A FX-1-MAJ-A RG-3-MAJ-A
   FX-1-MAJ-A RG-1-MAJ-A SEK-2-MAJ-A RG-6-MAJ-A FX-1-MAJ-C RG-2-MAJ-A
   SEK-2-MAJ-A RG-6-MAJ-A SEK-1-MAJ-A RG-6-MAJ-A PED-1-MAJ-A SEK-2-MAJ-A
   RG-6-MAJ-A FX-1-MAJ-A RG-2-MIN-A SEK-3-MIN-A RG-1-MIN-A))
 (VA
  (FX-1-MAJ-T RG-1-MAJ-T FX-1-MAJ-T 5-REST FX-1-MAJ-T 5-REST 2-REST 2-REST
   2-REST RG-5-MIN-T FX-1-MAJ-T RG-3-MAJ-T FX-1-MAJ-T RG-1-MAJ-T
   SEK-2-MAJ-T 2-REST 2-REST 5-REST SEK-2-MAJ-T 2-REST SEK-1-MAJ-T 2-REST
   PED-1-MAJ-T SEK-2-MAJ-T 2-REST FX-1-MAJ-T 5-REST SEK-3-MIN-T
   RG-1-MIN-T))
 (VC
  (2-REST 2-REST 2-REST RG-2-MAJ-T 2-REST RG-2-MIN-T FX-2-MIN-T RG-4-MIN-B
   FX-2-MIN-T RG-5-MIN-B 2-REST RG-3-MAJ-B 2-REST 2-REST SEK-2-MAJ-B
   RG-6-MAJ-B FX-2-MAJ-T RG-2-MAJ-T SEK-2-MAJ-B RG-6-MAJ-B SEK-1-MAJ-B
   RG-6-MAJ-B PED-1-MAJ-B SEK-2-MAJ-B RG-6-MAJ-B 2-REST RG-2-MIN-T
   SEK-3-MIN-B 2-REST)))

close

+ set-limits for the C-transpositions of the rthm-seqs

The next three variables form the core of this modular approach to creating tonal music with slippery chicken, and they involve the set-limits-high and set-limits-low slots of the slippery-chicken object.

By constraining the current set for each player in every sequence of the piece and setting the :avoid-melodic-octaves and :avoid-used-notes keyword arguments of the make-slippery-chicken function to NIL, the user can enable an exact mapping of the pitch-seq numbers of the rthm-seq objects to specific pitches in the current set.

Defining the pitch limits and storing them in an assoc-list

This demo piece does this by first creating an assoc-list object that stores the lowest and highest pitch of each rthm-seq in its C (0) transposition. Each key in this association list (which is assigned to the variable lims-al) is identical to the ID of one of the rthm-seqs defined in the rthm-seq-palette below.

       (lims-al 
        (make-assoc-list 'set-lims
                         '((2-rest (a4 a4))
                           (5-rest (a4 a4))
                           (fx-1-maj-c (fs4 a4))
                           (fx-1-maj-a (b3 e4))
                           (fx-1-maj-t (g3 c4))
                           (fx-1-min-c (fs4 af4))
                           (fx-1-min-a (b3 ef4))
                           (fx-1-min-t (g3 c4))
                           (fx-2-maj-t (g2 c3))
                           (fx-2-min-t (g2 c3))
                           (fx-3-maj-c (b4 e5))
                           (rg-1-maj-c (b4 c5))
                           (rg-1-maj-a (c4 g4))
                           (rg-1-maj-t (c3 g3)) 
                           (rg-1-min-c (b4 c5))
                           (rg-1-min-a (c4 g4))
                           (rg-1-min-t (c3 g3)) 
                           (rg-2-maj-c (ef4 c5))
                           (rg-2-maj-a (c4 f4))
                           (rg-2-maj-t (bf2 g3))
                           (rg-2-min-c (ef4 c5))
                           (rg-2-min-a (c4 f4))
                           (rg-2-min-t (bf2 g3))
                           (rg-3-maj-c (gs4 c5))
                           (rg-3-maj-a (e4 g4))
                           (rg-3-maj-t (b3 e4))
                           (rg-3-maj-b (a2 a3))
                           (rg-4-min-c (b4 c5))
                           (rg-4-min-a (c4 g4))
                           (rg-4-min-b (g2 g3))
                           (rg-5-min-c (b4 c5))
                           (rg-5-min-a (e4 g4))
                           (rg-5-min-t (c4 d4))
                           (rg-5-min-b (g2 af3))
                           (rg-6-maj-c (c5 g5))
                           (rg-6-maj-a (b4 c5))
                           (rg-6-maj-b (c3 g3))
                           (sek-1-maj-c (e5 f5))
                           (sek-1-maj-a (c5 c5))
                           (sek-1-maj-t (f4 g4))
                           (sek-1-maj-b (a2 c3))
                           (sek-2-maj-c (e5 g5))
                           (sek-2-maj-a (c5 d5))
                           (sek-2-maj-t (g4 a4))
                           (sek-2-maj-b (b2 c3))
                           (sek-3-min-c (ef5 f5))
                           (sek-3-min-a (c5 c5))
                           (sek-3-min-t (f4 g4))
                           (sek-3-min-b (af2 c3))
                           (ped-1-maj-c (c5 c5))
                           (ped-1-maj-a (g4 a4))
                           (ped-1-maj-t (e4 f4))
                           (ped-1-maj-b (c3 c3)))))

Creating the set-limits- lists

A small loop is then used to collect just the pitches, in MIDI-note number form, for the set-limits-low and set-limits-high slots for every sequence and every player in the piece. It collects them as MIDI-note numbers in order to facilitate their transposition in the next step.

       (lims-lo-hi
        (loop for n from 0 to 1
           collect 
             (loop for p in rsm
                collect 
                  (loop for rs in (second p)
                     collect (note-to-midi
                              (nth n (get-data-data rs lims-al)))))))

The list created consists of two lists, one for the low set limits and one for the high set limits, each of those lists in turn containing a list of pitches for each player, with one pitch for each sequence. This list is assigned to the variable lims-lo-hi. The result of this list in this instance with 29 sequences looks like this:

(((66 71 66 63 66 63 66 71 66 71 66 68 66 71 76 72 71 63 76 72 76 72 72 76
   72 66 63 75 71)
  (59 60 59 60 59 60 59 60 59 64 59 64 59 60 72 71 66 60 72 71 72 71 67 72
   71 59 60 72 60)
  (55 48 55 69 55 69 69 69 69 60 55 59 55 48 67 69 69 69 67 69 65 69 64 67
   69 55 69 65 48)
  (69 69 69 46 69 46 43 43 43 43 69 45 69 69 47 48 43 46 47 48 45 48 48 47
   48 69 46 44 69))
 ((69 72 69 72 69 72 68 72 68 72 69 72 69 72 79 79 76 72 79 79 77 79 72 79
   79 69 72 77 72)
  (64 67 64 65 64 65 63 67 63 67 64 67 64 67 74 72 69 65 74 72 72 72 69 74
   72 64 65 72 67)
  (60 55 60 69 60 69 69 69 69 62 60 64 60 55 69 69 69 69 69 69 67 69 65 69
   69 60 69 67 55)
  (69 69 69 55 69 55 48 55 48 56 69 57 69 69 48 55 48 55 48 55 48 55 48 48
   55 69 55 48 69)))

close

+ Transposing the set-limits- curves

Since some of the modules in this piece result in transpositions, as addressed above, the set limits too must be transposed in order to accurately apply to the transposed sets. This is done conveniently using a small loop, in which the integer from each two-item sublist of the transpositions variable is accessed and added to the MIDI-note numbers in the lims-lo-hi list.

       (transposed-lims-lo-hi
        (loop for hl in lims-lo-hi
           collect 
             (loop for p in hl
                for pl in '(vo vt va vc)
                collect
                  (list pl
                        (loop for n in p
                           for i in transpositions
                           for x from 1
                           collect x
                           collect (+ n (first i)))))))

This loop also adds the player ID and the sequence number for each pitch limit to its output, ensuring that the resulting list has two items at the top level: A properly-formatted list for both set-limits-low and set-limits-high, including player IDs and lists of x-y break-point pairs, that can be passed directly to the set-limits-low and -high slots from within the make-slippery-chicken function below.

(((VO
   (1 66 2 71 3 66 4 63 5 69 6 66 7 72 8 77 9 72 10 77 11 72 12 74 13 69
    14 74 15 79 16 75 17 74 18 66 19 82 20 78 21 82 22 78 23 78 24 82 25
    78 26 72 27 69 28 84 29 80))
  (VT
   (1 59 2 60 3 59 4 60 5 62 6 63 7 65 8 66 9 65 10 70 11 65 12 70 13 62
    14 63 15 75 16 74 17 69 18 63 19 78 20 77 21 78 22 77 23 73 24 78 25
    77 26 65 27 66 28 81 29 69))
  (VA
   (1 55 2 48 3 55 4 69 5 58 6 72 7 75 8 75 9 75 10 66 11 61 12 65 13 58
    14 51 15 70 16 72 17 72 18 72 19 73 20 75 21 71 22 75 23 70 24 73 25
    75 26 61 27 75 28 74 29 57))
  (VC
   (1 69 2 69 3 69 4 46 5 72 6 49 7 49 8 49 9 49 10 49 11 75 12 51 13 72
    14 72 15 50 16 51 17 46 18 49 19 53 20 54 21 51 22 54 23 54 24 53 25
    54 26 75 27 52 28 53 29 78)))
 ((VO
   (1 69 2 72 3 69 4 72 5 72 6 75 7 74 8 78 9 74 10 78 11 75 12 78 13 72
    14 75 15 82 16 82 17 79 18 75 19 85 20 85 21 83 22 85 23 78 24 85 25
    85 26 75 27 78 28 86 29 81))
  (VT
   (1 64 2 67 3 64 4 65 5 67 6 68 7 69 8 73 9 69 10 73 11 70 12 73 13 67
    14 70 15 77 16 75 17 72 18 68 19 80 20 78 21 78 22 78 23 75 24 80 25
    78 26 70 27 71 28 81 29 76))
  (VA
   (1 60 2 55 3 60 4 69 5 63 6 72 7 75 8 75 9 75 10 68 11 66 12 70 13 63
    14 58 15 72 16 72 17 72 18 72 19 75 20 75 21 73 22 75 23 71 24 75 25
    75 26 66 27 75 28 76 29 64))
  (VC
   (1 69 2 69 3 69 4 55 5 72 6 58 7 54 8 61 9 54 10 62 11 75 12 63 13 72
    14 72 15 51 16 58 17 51 18 58 19 54 20 61 21 54 22 61 23 54 24 54 25
    61 26 75 27 61 28 57 29 78))))

close

+ Variables for repetition in the rthm-seqs

The rhythms used in the rhtm-seq objects to create each voice of each module are highly repetitive. In order to make the rather long rthm-seq-palette both more efficient and more intelligible, a series of variables are created that not only store the more frequent rhythm combinations but also give them more recognizable names based on their function. In a few cases, the same rhythm combinations are given more than one variable name, as they serve more than one voice-leading function within the tonal modules that follow.

The time signature (ts) and ten "bare" (i.e., without a function ascribed to them) rhythms are defined (r-0 to r-9). These are then turned into longer sequences using Lisp's backquote and at-comma syntax for list splicing.

The terms "cantus", "altus", "tenor", and "bassus" are used to indicate the four voices, and the terms "cantizans", "altizans", "tenorizans", and "bassizans" are used to denote specific voice-leading structures.

       ;; time signature
       (ts '((4 4)))
       ;; basic rest rhythm
       (r-0 '((w)))
       ;; various multi-bar rest sequences
       (2-bars-rest-ts `((,@ts ,@r-0) ,r-0))
       (5-bars-rest-ts `((,@ts ,@r-0) ,r-0 ,r-0 ,r-0 ,r-0))
       ;; first three bare (functionless) rhythm sequences
       (r-1 '(q h q))
       (r-2 '(+q q h))
       (r-3 '(h h))
       ;; BASIC FAUXBOURDON RHYTHM SEQ
       ;; assigning functions (as variable names) to bare rhythm sequences
       (5-6-sus-7-6 `(,@r-1))
       (tie-to-cantizans `(,@r-2))
       ;; creating variables for fauxbourdon voices
       (sus-7-6-to-half-cadence-cntzns-ts `((,@ts ,@5-6-sus-7-6)
                                            ,tie-to-cantizans)) 
       (four-halves-to-half-cadence-altzns-tnzns-ts `((,@ts ,@r-3) ,r-3))
       ;; fourth and fifth bare rhythms
       (r-4 '(w))
       (r-5 '(- e e - q h))
       ;; BASIC 2-BAR RUGGIERO RHYTHM SEQ
       ;; assigning functions (as variable names) to bare rhythm sequences
       (cantizans-interruptus `(,@r-1))
       (whole `(,@r-4))
       ;; creating variables for 2-bar ruggiero voices
       (2-bar-cadence-cantizans-ts `((,@ts ,@cantizans-interruptus) ,whole))
       (2-bar-cadence-tenorizans-ts `((,@ts ,@r-5) ,whole))
       (2-bar-cadence-bassizans-ts `((,@ts ,@whole) ,whole))
       ;; sixth bare rhythm
       (r-6 '(q q h))
       ;; assigning functions (as variable names) to bare rhythm sequences       
       (tenorizans-interruptus-1 `(,@r-2))
       (tenorizans-interruptus-2 `(,@r-6))
       ;; BASIC 5-BAR FALLING-5THS RUGGIERO SEQUENCE
       ;; creating variables for 5-bar falling-5ths ruggiero voices
       (5-bars-falling-fifths-cntzns-tnzs-ts 
        `((,@ts ,@cantizans-interruptus) ,tenorizans-interruptus-1
          ,cantizans-interruptus ,tenorizans-interruptus-1 ,whole))
       (5-bars-falling-fifths-tnzs-cntzns-ts 
        `((,@ts ,@tenorizans-interruptus-2) ,cantizans-interruptus
          ,tenorizans-interruptus-1 ,cantizans-interruptus ,whole))
       (5-bars-falling-fifths-bsszns-ts 
        `((,@ts ,@whole) ,whole ,whole ,whole ,whole))
       ;; seventh bare rhythm
       (r-7 '(h. q))
       ;; 3-BAR RUGGIERO IV-I-V-I MODULATION SEQUENCE
       ;; creating variables for 3-bar ruggiero IV-I-V-I modulation voices 
       (two-halves `(,@r-3))
       ;; eighth and ninth bare rhythms
       (r-8 '(q q q q))
       (r-9 '(q q q. e))
       ;; NEIGHBOR ELABORATIONS FOR RUGGIERO VOICES
       ;; creating elaborated upper-/lower-neighbor 2-bar bassizans
       (2-bar-upper-lower-neighbor-octave-bassizans-ts `((,@ts ,@r-8) ,whole))
       ;; creating elaborated lower-neighbor 2-bar altizans and tenorizans
       (2-bar-lower-neighbor-altizans-ts `((,@ts ,@r-9) ,whole))
       (2-bar-lower-neighbor-tenorizans-ts `((,@ts ,@r-6) ,whole))
       ;; BASIC 2-BAR SEKUND-AKKORD SEQUENCE
       ;; creating variable for basic sekund-akkord sequence
       (sekund-core `((,@ts ,@r-3) ,whole))
       ;; BASIC 2-BAR PEDAL-POINT SEQUENCE
       ;; creating variables for the inner and outer voices of the basic 2-bar
       ;; pedal-point sequence
       (2-bar-pedal-sustained-outer-voices-ts `((,@ts ,@whole) ,whole))
       (2-bar-pedal-moving-inner-halves-ts `((,@ts ,@r-3) ,whole))

close

+ The call to make-slippery-chicken

The call to make-slippery-chicken is then also made within the let* enclosure, and its resulting object is stored in the variable tonal-example.

The majority of the keyword arguments within the function are set by using the variables defined above. Only the rthm-seq-palette is defined within the function itself.

       (tonal-example
        (make-slippery-chicken
         '+tonal-example+
         :title "tonal example"
         :tempo-map '((1 (q 112)))
         :ensemble '(((vo (violin :midi-channel 1))
                      (vt (violin :midi-channel 2))
                      (va (viola :midi-channel 3))
                      (vc (cello :midi-channel 4))))
         :set-palette sp
         :set-map `((1 ,sm))
         :set-limits-low (first transposed-lims-lo-hi)
         :set-limits-high (second transposed-lims-lo-hi)
         :avoid-melodic-octaves nil
         :avoid-used-notes nil
         [...]
         :rthm-seq-map `((1 ,rsm))))

close

+ The individual voices in the rthm-seq-palette

Each rthm-seq in the rthm-seq-palette then consists of one voice of one of the tonal 3- and 4-voiced modules. They are grouped together according to similar types (fauxbourdon, ruggiero etc.)

How the IDs are created

The IDs of these rthm-seqs are selected based on the module type, the variation number, the mode, and the voice. In this case, as above, the top voice is abbreviated "c" for cantus; and the remaining voices are abbreviated "a", "t", and "b" for "altus", "tenor", and "bassus". These IDs are used in the combos-al variable defined above when automatically generating the rthm-seq-map

Splicing in the variables for longer rhythm patterns

The majority of the rthm-seq objects defined here make use of the variables declared above for the multi-bar combinations of repeating rhythm patterns. They are spliced into the definition of the rthm-seq objects using Lisp's backquote and comma syntax.

           (fx-1-maj-c (,sus-7-6-to-half-cadence-cntzns-ts

Implementing the pitches-to-pseq function

The rthm-seq objects defined in this piece each have only one pitch-seq in their pitch-seq-palette. This one pitch-seq always applies to one of the two core-set- variables, which are both the C/0 transpositions of the given sets. By populating the set-palette with exact copies of the original two sets, which are merely transposed, the interval structure of each set will always remain the same, and the pitch-seq numbers will always return the pitch on the same scale degree in the transposed set.

The pitch-seq numbers are generated automatically using the pitches-to-pseq function defined above. Each :pitch-seq-palette is created by splicing the results of calling that function with specified note-name symbols (as they would apply to the C/0 transposition of the module) and the core-set variable to which they apply into the pitch-seq-palette, again using Lisp's backquote and comma syntax.

                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g4 a4 g4 fs4 g4) core-set-maj))))

The rthm-seq-palette in full:

         :rthm-seq-palette 
         ;; The rthm-seqs: "-c" means cantus, "-a" means alto, "-t" means
         ;; tenor, "-b" means bass.
         `((2-rest (,2-bars-rest-ts))
           (5-rest (,5-bars-rest-ts))
           ;; FAUXBOURDON SEQUENCES
           ;; fauxbourdon 1 major
           (fx-1-maj-c (,sus-7-6-to-half-cadence-cntzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g4 a4 g4 fs4 g4) core-set-maj))))
           (fx-1-maj-a (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(e4 d4 c4 b3) core-set-maj))))
           (fx-1-maj-t (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(c4 b3 a3 g3) core-set-maj))))
           ;; fauxbourdon 1 minor
           (fx-1-min-c (,sus-7-6-to-half-cadence-cntzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g4 af4 g4 fs4 g4) core-set-min))))
           (fx-1-min-a (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(ef4 d4 c4 b3) core-set-min))))
           (fx-1-min-t (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(c4 bf3 af3 g3) core-set-min))))
           ;; fauxbourdon 2 major
           (fx-2-maj-t (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(c3 b2 a2 g2) core-set-maj))))
           ;; fauxbourdon 2 minor
           (fx-2-min-t (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(c3 bf2 af2 g2) core-set-min))))
           ;; fauxbourdon 3 major
           (fx-3-maj-c (,four-halves-to-half-cadence-altzns-tnzns-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(e5 d5 c5 b4) core-set-maj))))
           ;; RUGGIERO SEQUENCES
           ;; ruggiero 1 major
           (rg-1-maj-c (,2-bar-cadence-cantizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(b4 c5 b4 c5) core-set-maj))))
           (rg-1-maj-a (,2-bar-cadence-tenorizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g4 f4 e4 d4 c4) core-set-maj))))
           (rg-1-maj-t (,2-bar-cadence-bassizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g3 c3) core-set-maj))))
           ;; ruggiero 1 minor
           (rg-1-min-c (,2-bar-cadence-cantizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(b4 c5 b4 c5) core-set-min))))
           (rg-1-min-a (,2-bar-cadence-tenorizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g4 f4 ef4 d4 c4) core-set-min))))
           (rg-1-min-t (,2-bar-cadence-bassizans-ts
                        :pitch-seq-palette 
                        (,(pitches-to-pseq '(g3 c3) core-set-min))))
           ;; ruggiero 2 major
           (rg-2-maj-c (,5-bars-falling-fifths-cntzns-tnzs-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 bf4 a4 g4 a4 bf4 af4 g4 f4
                                             ef4) core-set-maj))))
           (rg-2-maj-a (,5-bars-falling-fifths-tnzs-cntzns-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(f4 e4 d4 e4 f4 ef4 d4 c4 d4 ef4 d4
                                             ef4) core-set-maj))))
           (rg-2-maj-t (,5-bars-falling-fifths-bsszns-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 c3 f3 bf2 ef3) core-set-maj))))
           ;; ruggiero 2 minor
           (rg-2-min-c (,5-bars-falling-fifths-cntzns-tnzs-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 bf4 a4 g4 a4 bf4 af4 gf4 f4
                                             ef4) core-set-maj)))) 
           (rg-2-min-a (,5-bars-falling-fifths-tnzs-cntzns-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(f4 e4 d4 e4 f4 ef4 d4 c4 d4 ef4
                                             d4 ef4) core-set-maj))))
           (rg-2-min-t (,5-bars-falling-fifths-bsszns-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 c3 f3 bf2 ef3) core-set-maj))))
           ;; ruggiero 3 major
           (rg-3-maj-c (((,@ts ,@cantizans-interruptus) ,whole
                         ,tie-to-cantizans) 
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 bf4 a4 gs4 a4)
                                           core-set-maj))))
           (rg-3-maj-a (((,@ts ,@r-6) ,tenorizans-interruptus-1 ,two-halves)
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g4 g4 g4 f4 e4 e4 e4)
                                           core-set-maj))))
           (rg-3-maj-t (((,@ts ,@r-6) ,r-7 ,two-halves)
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(d4 e4 d4 d4 c4 b3 cs4)
                                           core-set-maj))))
           (rg-3-maj-b (((,@ts ,@whole) ,two-halves ,two-halves)
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 d3 a3 e3 a2) core-set-maj))))
           ;; ruggiero 4 minor
           (rg-4-min-c (,2-bar-cadence-cantizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 b4 c5) core-set-min))))
           (rg-4-min-a (,2-bar-cadence-tenorizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g4 f4 ef4 d4 c4) core-set-min))))
           (rg-4-min-b (,2-bar-upper-lower-neighbor-octave-bassizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 fs3 g3 g2 c3) core-set-min))))
           ;; ruggiero 5 minor
           (rg-5-min-c (,2-bar-cadence-cantizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 b4 c5) core-set-min))))
           (rg-5-min-a (,2-bar-lower-neighbor-altizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g4 fs4 g4 f4 e4) core-set-min))))
           (rg-5-min-t (,2-bar-lower-neighbor-tenorizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(d4 c4 d4 c4) core-set-min))))
           (rg-5-min-b (,2-bar-upper-lower-neighbor-octave-bassizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 af3 g3 g2 c3) core-set-min))))
           ;; ruggiero 6 major
           (rg-6-maj-c (,2-bar-cadence-tenorizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g5 f5 e5 d5 c5) core-set-maj))))
           (rg-6-maj-a (,2-bar-cadence-cantizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(b4 c5 b4 c5) core-set-maj))))
           (rg-6-maj-b (,2-bar-cadence-bassizans-ts
                        :pitch-seq-palette
                        (,(pitches-to-pseq '(g3 c3) core-set-maj))))
           ;; SEKUND SEQUENCES
           ;; sekund 1 major
           (sek-1-maj-c (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(e5 e5 f5) core-set-maj))))
           (sek-1-maj-a (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c5 c5 c5) core-set-maj))))
           (sek-1-maj-t (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(g4 g4 f4) core-set-maj))))
           (sek-1-maj-b (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c3 bf2 a2) core-set-maj))))
           ;; sekund 1 major
           (sek-2-maj-c (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(e5 fs5 g5) core-set-maj))))
           (sek-2-maj-a (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c4 d5 d5) core-set-maj))))
           (sek-2-maj-t (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(g4 a4 g4) core-set-maj))))
           (sek-2-maj-b (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c3 c3 b2) core-set-maj))))
           ;; sekund 3 minor
           (sek-3-min-c (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(ef5 e5 f5) core-set-min))))
           (sek-3-min-a (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c5 c5 c5) core-set-min))))
           (sek-3-min-t (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(g4 g4 f4) core-set-min))))
           (sek-3-min-b (,sekund-core
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c3 bf2 af2) core-set-min))))
           ;; PEDAL SEQUENCES
           ;; pedal 1 major
           (ped-1-maj-c (,2-bar-pedal-sustained-outer-voices-ts
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c5 c5) core-set-maj))))
           (ped-1-maj-a (,2-bar-pedal-moving-inner-halves-ts
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(g4 a4 g4) core-set-maj))))
           (ped-1-maj-t (,2-bar-pedal-moving-inner-halves-ts
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(e4 f4 e4) core-set-maj))))
           (ped-1-maj-b (,2-bar-pedal-sustained-outer-voices-ts
                         :pitch-seq-palette
                         (,(pitches-to-pseq '(c3 c3) core-set-maj)))))

close

+ Enharmonic spellings that reflect the tonality

By default, slippery chicken will automatically attempt to respell all of the pitches in the resulting score in a manner that makes sense in an atonal setting. The result of that automatic procedure in this example results in a number of pitches needing to be enharmonically flipped, in order to accurately reflect their function within a tonal setting.

There are three options for changing the enharmonic pitches of one or more notes in a slippery-chicken object: the respell-notes, enharmonic, and enharmonics methods. This example uses the latter two methods.

In order for these three methods to be effective, they must be called as post-generation editing methods prior to the calls to any of the printable output methods, and therefore require that the respell-notes option of cmn-display and write-lp-data-for-all be set to NIL.

  (cmn-display tonal-example :file "/tmp/tonal.eps" 
               :auto-bar-nums 5
               :respell-notes nil
               :auto-clefs nil)
  (write-lp-data-for-all tonal-example
                         :respell-notes nil
                         :auto-clefs nil))

enharmonics

The enharmonics method allows the user to specify a range of measures in which pitches are to be enharmonically flipped, as well as a list of note-name symbols that are to be affected. The code for this piece therefore uses a loop in which the starting and ending bars, the player, and the list of pitches, are passed as a for value to the macro, and a do statement that applies the enharmonics method with these different values at each iteration. This method does not allow for the enharmonic spelling of "white-key" notes (e.g. C as B-sharp etc.)

  (loop for d in '((14 16 vo (cs5)) (15 16 vt (fs4)) (19 27 vt (af4))
                   (25 25 va (af4)) (27 28 vo (cs5)) (27 30 vt (fs4 cs5)) 
                   (27 30 va (fs4)) (42 45 vo (cs5)) (43 44 vt (fs4))
                   (47 59 vo (bf5 af5)) (47 59 vt (ef5 af5)) 
                   (47 59 va (bf4 ef5)) (61 65 vo (ef5)) (61 66 vt (bf4 af4))
                   (70 70 vo (af5)))
     do 
       (enharmonics tonal-example (first d) (second d) (third d) 
                    :pitches (fourth d)))

enharmonic

The enharmonic method always applies to only one sounding event object at a time. The optional :force-naturals argument of this method allows the method to be used to respell "white-key" pitches as enharmonic equivalents. The method is used here as well within a loop whose for statement specifies a list of sublists consisting of the measure number, event number and player ID of the notes to be altered.

  (loop for en in '((17 1 vt) (17 3 vo) (17 3 vt) (20 2 vo) (20 2 vt) (21 1 vo)
                    (21 3 vo) (21 2 vc) (24 2 vo) (24 2 vt) (25 1 vo) (25 3 vo)
                    (25 2 vt) (26 1 vt) (27 2 vt) (28 2 va) (29 2 vo) (29 3 vo)
                    (29 1 vc) (30 2 vt) (45 2 vo) (45 3 vo) (45 1 vt) (45 3 vt)
                    (47 2 vo) (48 1 vc) (49 1 vt) (49 3 vt) (52 1 vc) (53 1 vt)
                    (53 3 vt) (57 2 vo) (58 1 vc) (59 1 vt) (59 3 vt) (62 2 vo)
                    (63 1 vo) (62 2 vt) (61 2 va) (62 1 va))
     do (enharmonic (get-event tonal-example (first en) (second en) (third en))
                    :force-naturals t))

close

+ Inserting key changes and double-bars

Another small loop is used to insert key changes and double-bar lines into the printable output. It is important to point out here that CMN always prints the accidentals into the score, even if a key signature has been added; whereas, LilyPond will hide accidentals already present in a key signature and automatically add cautionary accidentals where suited.

This little loop takes care of both the key signature and the double-bar placement at the same time.

  (loop for b-k in '((12 (ef major)) (19 (fs minor)) (27 (gf major))
                     (32 (ef major)) (47 (fs major)) (68 (a minor)))
     do
       (loop for p in '(vo vt va vc)
          do
            (change-bar-line-type tonal-example (1- (first b-k)) 1)
            (add-mark-before 
             (get-event tonal-example (first b-k) 1 p)
             `(key ,@(second b-k)))))

close

+ Changing staff names for same instruments

The final step undertaken by the code for this piece is to change the instrument staff names of the two violin parts for the printable output. By default these will both be identified in the score as violin. The following little loop uses setf to change the value of the staff-name and staff-short-name slots of the corresponding instrument objects stored in this slippery-chicken object's ensemble slot to violin 1 and violin 2.

  (loop for n in '((vo "violin 1" "vln 1") (vt "violin 2" "vln 2"))
     do 
       (setf (staff-name 
              (get-data-data (first n) (ensemble tonal-example))) 
             (second n))
       (setf (staff-short-name 
              (get-data-data (first n) (ensemble tonal-example)))
             (third n)))

close

+ Output

The output for this piece is generated in the same way already covered in other tutorials. Of note here is the use of the auto-bar-nums argument to the cmn-display method, which causes bar numbers to be placed into the score at intervals of 5 measures, and the setting of both the respell-notes and auto-clefs options to NIL in both the cmn-display and write-lp-data-for-all methods. The former is necessary in order for the enharmonic changes described above to be effective. The latter is merely a matter of preference.

close