"Primary Disposition" — A guide to the core usage of slippery chicken

+ Associated files

close


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

This document assumes you have familiarised yourself with the overview and essential concepts of slippery chicken.

The following is a brief introduction to the most basic use of slippery chicken's core features. In this example all of the set palettes and maps, rhythm palettes and maps, pitch contours, articulation, and dynamics are entered by hand. This should provide the beginning user with an understanding of the framework that underlies slippery chicken. More advanced users will most probably begin replacing the hand-typed data with Lisp routines that will generate it for them.

While slippery chicken is not limited to any specific style, it has so far been used for abstract, atonal, and microtonal avant-garde compositions. This first example, however, will be a piece in a slightly more minimalist style.

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

(in-package :sc)

(in-scale :chromatic)

(make-slippery-chicken  
 '+primary-disposition+ 
 :title "Primary Disposition" 
 :instrument-palette +slippery-chicken-standard-instrument-palette+ 
 :ensemble '(((flt (flute :midi-channel 1))
              (clr (b-flat-clarinet :midi-channel 2))
              (vln-one (violin :midi-channel 3)) 
              (vla (viola :midi-channel 4)) 
              (cel (cello :midi-channel 5))))
 :staff-groupings '(2 3)
 :tempo-map '((1 (q 84)))
 :set-palette '((set1 ((fs2 b2 d4 a4 d5 e5 a5 d6))) 
                (set2 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6)))
                (set3 ((cs3 fs3 e4 a4 e5 a5 e6))) 
                (set4 ((fs2 cs3 e4 a4 b4 e5 a5 b5 e6)))) 
 :set-map '((1 (set1 set1 set2 set1 set2)) 
            (2 (set2 set2 set3 set2 set3 set2 set3)) 
            (3 (set3 set2 set4 set2 set4 set3 set4 set4 set2 set3 set4))
            (4 (set4 set1 set4 set1 set4 set1 set1)))   
 :set-limits-high '((vla (0 b4 100 b4))
                    (cel (0 f4 100 f4)))
 :rthm-seq-palette
 '((seq1 ((((4 4) - 16 16 8 - { 5 - 20 10 20 20 - } { 3 3 6 } )   
           ( - s s s s - (s) - s s s - - +e. s - q))   
          :pitch-seq-palette (1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9)
          :marks (mp 1 a 1 s 3 slur 1 3 a 4 slur 4 7 s 9 a 10 slur 16 18)))  
   (seq2 ((((4 4) - e e - - s s s s - (s) s (s) s { 3 - te te te - } )  
           ( - s s s - (s)  { 3 - te te - (te) } q \+8 (e) )) 
          :pitch-seq-palette (11 10 9 11 10 8 7 11 10 6 5 11 10 9 8 7 11) 
          :marks (mf 1 s 1 2 a 3 slur 3 4 a 5 slur 5 6 s 7 s 8 a 9 
                     slur 9 11 a 12 slur 12 14 s 15 16 a 17))) 
   (seq3 ((((4 4) { 3 - te te te - } - e e - - s s s s - (s) s (s) s ) 
           (- s s s s - - s s s s - { 3 - te te te - } { 3 - +te te te - } ))
          :pitch-seq-palette (1 2 1 2 3 1 2 3 4 5 6 1 2 3 5 4 4 4 2 7 5 4 5 6)   
          :marks (f 1 slur 1 3 a 4 s 4 5 slur 6 7 slur 8 9 s 10 11 
                    slur 12 13 slur 14 15 a 16 slur 18 19 slur 20 21 a 22 
                    slur 24 25)))  
   (seq4 ((((4 4) - s s (s) s - - s s - (e) - e s - (s) { 3 - te te te - } )
           ( { 5 - 10 10 20 - } - +s s s s - (e) - e+32 32 s s s - ))
          :pitch-seq-palette (4 5 4 4 5 4 5 4 7 13 
                                17 15 14 15 11 12 13 4 5 7 8) 
          :marks (ff 1 slur 1 2 s 3 slur 4 5 a 6 slur 8 10 s 10 a 11 s 11 
                     a 12 s 12 slur 13 15 slur 16 17 a 18 slur 19 22))))
 :rthm-seq-map
 '((1 ((flt (seq1 seq1 seq2 seq1 seq2)) 
       (clr (seq1 seq2 seq1 seq2 seq1)) 
       (vln-one (seq1 seq1 seq2 seq1 seq2))   
       (vla (seq1 seq2 seq1 seq2 seq1))   
       (cel (seq1 seq1 seq2 seq1 seq2))))
   (2 ((flt (seq1 seq1 seq2 seq1 seq2 seq1 seq2))
       (clr (seq1 seq1 seq3 seq1 seq3 seq1 seq3))
       (vln-one (seq2 seq2 seq3 seq2 seq3 seq2 seq3))  
       (vla (seq2 seq2 seq1 seq2 seq1 seq2 seq1))  
       (cel (seq3 seq3 seq2 seq3 seq2 seq3 seq2))))  
   (3 ((flt (seq1 seq1 seq2 seq1 seq2 seq1 seq2 seq2 seq1 seq2 seq2))
       (clr (seq2 seq2 seq3 seq2 seq3 seq2 seq3 seq3 seq2 seq3 seq3))
       (vln-one (seq3 seq3 seq4 seq3 seq4 seq3 seq4 seq4 seq3 seq4 seq4)) 
       (vla (seq4 seq4 seq3 seq4 seq3 seq4 seq3 seq3 seq4 seq3 seq3))  
       (cel (seq4 seq4 seq2 seq4 seq2 seq4 seq2 seq2 seq4 seq2 seq2))))  
   (4 ((flt (seq4 seq3 seq4 seq1 seq4 seq1 seq1))  
       (clr (seq3 seq2 seq3 seq2 seq1 seq2 seq1))
       (vln-one (seq2 seq1 seq1 seq1 seq2 seq1 seq1)) 
       (vla (seq4 seq3 seq4 seq2 seq1 seq1 seq1)) 
       (cel (seq2 seq2 seq2 seq1 seq2 seq1 seq1))))))

(midi-play +primary-disposition+ :midi-file "/tmp/primary-disposition.mid")

(cmn-display +primary-disposition+ :file "/tmp/primary-disposition.eps")

(write-lp-data-for-all +primary-disposition+ :base-path "/tmp/")

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

;;; EOF primary-disposition.lsp

close

The code explained

Setting the environment

The first two lines in this example set two global parameters of the Lisp environment in which slippery chicken will generate the musical data and any accompanying output. It is encouraged to include these in each slippery chicken file.

The first of these:

(in-package :sc)

…instructs the Lisp environment to process the following code within the sc (slippery chicken) package.

The second:

(in-scale :chromatic)

…instructs slippery chicken to use the chromatic scale as the #<tuning> environment when processing all note-name symbols. See the page on note-names and scales for more detail on global #<tuning> environments.

The call to make-slippery-chicken

First the make-slippery-chicken function is called. This creates a slippery chicken object, which holds all of the data necessary to generate the piece.

(make-slippery-chicken

The first argument to the make-slippery-chicken function is a global variable that is defined for the music created (the slippery chicken object). The global nature of this variable allows for editing and manipulating the musical data after it is has been generated.

 '+primary-disposition+

The next argument sets the title of the composition, which is used both for the title of the piece within the generated sheet music output as well as for the names of the LilyPond files automatically generated by slippery chicken (if that feature is used).

 :title "Primary-Disposition"

The instruments and ensemble

Next, the instruments and ensemble are specified. The ensemble is made up of players, each of which can be assigned one or more instruments. For this example, each player plays only one instrument.

Instruments

The instrument-palette argument points to the instrument-palette used for the given piece. An instrument-palette contains the list of instrument objects used in the ensemble, with definitions of attributes such as pitch ranges, transpositions, and score names and abbreviations, etc. This piece uses the +slippery-chicken-standard-instrument-palette+, which is defined in the file instruments.lsp and automatically assigned to this global variable when slippery chicken is loaded.

 :instrument-palette
        +slippery-chicken-standard-instrument-palette+

A description of how to modify the attributes of instruments in a given palette for the scope of a given composition, as well as how to create a new palette or combine existing palettes, can be found on the page on tailoring instrument definitions.

Ensemble

In the ensemble block, IDs are defined for each of the ensemble's players, and instruments from the instrument-palette are then assigned to each player. Indications for the MIDI channels on which the instruments are to be played back can be entered here as well, if MIDI output is desired. The ID chosen by the user for the player (e.g. flt) can be any arbitrary alpha-numeric combination, and will be used as a reference within the rthm-seq-map section of the call to the make-slippery-chicken function later. The name of the instrument must be the ID of the given instrument object exactly as it is defined in the instrument-palette used.

NB: If LilyPond is to be used for printable output, the names chosen for the ensemble's players cannot contain numbers (e.g. violin1), as LilyPond's parser does not accept them. One option is to use hyphenated alphabetical characters instead, such as violin-one. Note that this does not refer to staff-names of instruments, just the symbol that identified a player in the ensemble, rthm-seq-palette, etc. (See nouveau reich for an example of changing score staff names.)

 :ensemble '(((flt (flute :midi-channel 1))
              (clr (b-flat-clarinet :midi-channel 2))
              (vln-one (violin :midi-channel 3)) 
              (vla (viola :midi-channel 4)) 
              (cel (cello :midi-channel 5))))
Staff groupings for printed output

Should printed output be desired, the staff grouping layout of the score can be indicated using the :staff-groupings argument. This argument takes a list of the number of consecutive players grouped together from top to bottom, as they appear in the ensemble block. Thus, the following code indicates that in the score, the players of the ensemble are to be grouped into 2 instruments, and 3 instruments (flt with clr, and vln-one, vla, cel).

 :staff-groupings '(2 3)

Tempo

The next argument is used to set the tempo. This example uses the :tempo-map argument for this purpose (see the page on tempo for other options). The :tempo-map argument takes a list of 2-item lists. The first item in each of these lists is the measure number at which the subsequent tempo indication is to begin. A starting tempo attached to the first measure at the very least must be specified here. The second item is the tempo indication itself, which takes a beat-type indicator paired with the number of beats per minute. The beat-type can be indicated using either numeric (e.g. 4 for a quarter-note) or alphabetic rhythm representations (e.g. q for a quarter note). (See the page on rhythms for more detail on rhythmic representations in slippery chicken).

The tempo-map stated here thus means that the tempo will be quarter = 84 starting in measure 1. This example will use just one tempo for the entire piece.

 :tempo-map '((1 (q 84)))

Pitch collections

The pitch (or "set") data for a given piece is entered in the set-palette and set-map blocks. Each of the sets in the set-palette block defines a limited collection of pitches. The first element of each list in the set-palette block is the set's ID, which is chosen by the user and can consist of any alpha-numeric combination. This ID will then be referenced from within the set-map block when mapping the sets to the piece's structure. The pitches are entered here using the note-naming conventions spelled out in the documentation for note-names and scales.

This example defines pentatonic sets:

 :set-palette '((set1 ((fs2 b2 d4 a4 d5 e5 a5 d6))) 
                (set2 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6)))
                (set3 ((cs3 fs3 e4 a4 e5 a5 e6))) 
                (set4 ((fs2 cs3 e4 a4 b4 e5 a5 b5 e6))))

The set-map block then determines which sets of pitches are available for each sequence in the piece. Any given sequence will contain only pitches from the corresponding set referred to in the set-map for that sequence.

The first number of each list in the set-map is the ID assigned to that section of the piece, and corresponds to the section ID of the other -maps in the slippery-chicken object being created. Section IDs must be numerical values only. The list following the section ID is a series of the IDs defined for the sets in the set-palette. These can be used in any order in the set-map. They apply to the entire ensemble for the corresponding sequence within the composition.

 :set-map '((1 (set1 set1 set2 set1 set2)) 
            (2 (set2 set2 set3 set2 set3 set2 set3)) 
            (3 (set3 set2 set4 set2 set4 set3 set4 set4 set2 set3 set4))
            (4 (set4 set1 set4 set1 set4 set1 set1)))
Limiting an instrument's pitch range

The pitch ranges of the instruments in the +slippery-chicken-standard-instrument-palette+ are defined to include the absolute spectrum of pitches available to the instruments. This can occasionally result in slippery chicken choosing pitches from the extreme ends of the pitch ranges. A number of options are available for addressing this. For this piece, set-limits-high is used to limit the upper range of the cello to F4 and that of the viola to B4.

 :set-limits-high '((vla (0 b4 100 b4))
                    (cel (0 f4 100 f4)))

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

Rhythms

The rhythmic data for the piece is defined in the rthm-seq-palette. The rthm-seq-palette consists of a list of rhythm sequences (rthm-seq objects), which are enclosed in separate sets of parentheses. As with the set-palette, each rthm-seq is given an ID by the user, and these IDs too can consist of any alpha-numeric combination.

The individual rthm-seq objects consist of a list of numbers and/or letters that represent rhythmic durations. Each rthm-seq can be one or more measures long, and each measure may start with a time signature (see the documentation on the rthm-seq object for more detail on how to construct a rthm-seq). Rhythmic values can be written in numeric form or using the alphabetic nomenclature (see the documentation on rhythms for more detail on rhythm-naming conventions).

 :rthm-seq-palette
'((seq1 ((((4 4) - 16 16 8 - { 5 - 20 10 20 20 - } { 3 3 6 } )   
           ( - s s s s - (s) - s s s - - +e. s - q))

Pitch curves

Each rthm-seq in the rthm-seq-palette also requires indications for the melodic contour of the pitches that slippery chicken will automatically assign to the rhythms it contains. This is done by defining a pitch-seq-palette within each rthm-seq.

The list of numbers in the pitch-seq-palette represent a general pitch linear contour that is scaled to the available pitches for each instrument playing that rthm-seq in any given sequence. The difference between two consecutive numbers in the pitch-seq-palette indicates the direction and relative size of the interval between the corresponding two pitches selected by slippery chicken. The actual pitches selected by slippery chicken from the current set will depend on a number of factors, including the range of the instrument playing them.

See the page on pitches for more on understanding pitch-seq curves and more detail on how slippery chicken selects pitches.

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

Articulation, dynamics, slurs

Indications for articulation, dynamics, slurs and many performance techniques for both CMN and LilyPond output can also be enclosed within each individual rthm-seq of the rthm-seq-palete. This is done using the :marks keyword followed by a list of the marks to be added (such as a for an accent or s for staccato) paired with index numbers for the specific rhythm items within the rthm-seq to which the marks are to be attached.

More about adding marks to a score can be found on the page about articulation, dynamics, and performance techniques. A list of the marks available can be found on the tables of marks portion of that same page.

NB: Marks are attached to rhythm items of the rthm-seq, and not to items in the pitch-seq of the pitch-seq-palette. Two tied rhythmic values are counted as separate items for this function.

 :marks (mp 1 a 1 s 3 slur 1 3 a 4 slur 4 7 s 9 a 10 slur 16 18)))

The rest of the rthm-seq-palette in this example is constructed in the same manner:

   (seq2 ((((4 4) - e e - - s s s s - (s) s (s) s { 3 - te te te - } )  
           ( - s s s - (s)  { 3 - te te - (te) } q \+8 (e) )) 
          :pitch-seq-palette (11 10 9 11 10 8 7 11 10 6 5 11 10 9 8 7 11) 
          :marks (mf 1 s 1 2 a 3 slur 3 4 a 5 slur 5 6 s 7 s 8 a 9 
                     slur 9 11 a 12 slur 12 14 s 15 16 a 17))) 
   (seq3 ((((4 4) { 3 - te te te - } - e e - - s s s s - (s) s (s) s ) 
           (- s s s s - - s s s s - { 3 - te te te - } { 3 - +te te te - } ))
          :pitch-seq-palette (1 2 1 2 3 1 2 3 4 5 6 1 2 3 5 4 4 4 2 7 5 4 5 6)   
          :marks (f 1 slur 1 3 a 4 s 4 5 slur 6 7 slur 8 9 s 10 11 
                    slur 12 13 slur 14 15 a 16 slur 18 19 slur 20 21 a 22 
                    slur 24 25)))  
   (seq4 ((((4 4) - s s (s) s - - s s - (e) - e s - (s) { 3 - te te te - } )
           ( { 5 - 10 10 20 - } - +s s s s - (e) - e+32 32 s s s - ))
          :pitch-seq-palette (4 5 4 4 5 4 5 4 7 13 
                                17 15 14 15 11 12 13 4 5 7 8) 
          :marks (ff 1 slur 1 2 s 3 slur 4 5 a 6 slur 8 10 s 10 a 11 s 11 
                     a 12 s 12 slur 13 15 slur 16 17 a 18 slur 19 22))))

The structure

The last major block of the make-slippery-chicken function is the rthm-seq-map. This is where the individual rthm-seq objects, with their pitch contours and marks, are assembled into the desired order to create the structure of the piece.

There must be exactly the same number of sections in this map as there are in the set-map, and the section IDs must be the same. Following the section ID, the first item in each list of a given section is then the player (e.g. flt), as named in the ensemble block. The items within the lists associated with each player must consist of IDs assigned to the individual rthm-seq objects in the rthm-seq-palette. There must be exactly the same number of rthm-seq IDs for each player of each section as there are set IDs in the set-map for the corresponding section.

All simultaneously occurring rthm-seq objects in the rthm-seq-map must be of equal length—i.e. same number of bars and same metrical structure. For this piece, all rthm-seq objects have been made equal in length and meter in the rthm-seq-palette so that all combinations are possible.

See the documentation on rthm-seq-maps for more detail.

 :rthm-seq-map
 '((1 ((flt (seq1 seq1 seq2 seq1 seq2)) 
       (clr (seq1 seq2 seq1 seq2 seq1)) 
       (vln-one (seq1 seq1 seq2 seq1 seq2))   
       (vla (seq1 seq2 seq1 seq2 seq1))   
       (cel (seq1 seq1 seq2 seq1 seq2))))
   (2 ((flt (seq1 seq1 seq2 seq1 seq2 seq1 seq2))
       (clr (seq1 seq1 seq3 seq1 seq3 seq1 seq3))
       (vln-one (seq2 seq2 seq3 seq2 seq3 seq2 seq3))  
       (vla (seq2 seq2 seq1 seq2 seq1 seq2 seq1))  
       (cel (seq3 seq3 seq2 seq3 seq2 seq3 seq2))))  
   (3 ((flt (seq1 seq1 seq2 seq1 seq2 seq1 seq2 seq2 seq1 seq2 seq2))
       (clr (seq2 seq2 seq3 seq2 seq3 seq2 seq3 seq3 seq2 seq3 seq3))
       (vln-one (seq3 seq3 seq4 seq3 seq4 seq3 seq4 seq4 seq3 seq4 seq4)) 
       (vla (seq4 seq4 seq3 seq4 seq3 seq4 seq3 seq3 seq4 seq3 seq3))  
       (cel (seq4 seq4 seq2 seq4 seq2 seq4 seq2 seq2 seq4 seq2 seq2))))  
   (4 ((flt (seq4 seq3 seq4 seq1 seq4 seq1 seq1))  
       (clr (seq3 seq2 seq3 seq2 seq1 seq2 seq1))
       (vln-one (seq2 seq1 seq1 seq1 seq2 seq1 seq1)) 
       (vla (seq4 seq3 seq4 seq2 seq1 seq1 seq1)) 
       (cel (seq2 seq2 seq2 seq1 seq2 seq1 seq1))))))

The output

The make-slippery-chicken function allocates all of the musical data it generates to a global variable created from the first argument that it is passed. This makes it possible to generate the various forms of output separately, after the generation of the musical data, as is seen here.

More information on the various forms of output can be found on the output page.

MIDI

MIDI output can be generated by slippery chicken using the midi-play method. This method takes as its only required argument the global variable defined as the first argument to the make-slippery-chicken function. The other argument specified here, :midi-file, is the directory path and file name for the output to be generated (which defaults to "/tmp/sc.mid" if not specified). The file name should end with the suffix .mid for ease of opening after generation.

(midi-play +primary-disposition+ :midi-file "/tmp/primary-disposition.mid")

See the User Guide entry for MIDI for more details on the other arguments to midi-play.

Common Music Notation

The first printable output option in this example is that of Common Music Notation using the cmn-display method. This method, too, takes as its first argument the variable defined as the first argument to the make-slippery-chicken function. The :file argument allows the user to specify the directory path and file name for the output generated. CMN produces encapsulated post-script files, so adding the .eps suffix to the file name will facilitate opening the file later.

(cmn-display +primary-disposition+ :file "/tmp/primary-disposition.eps")

See the User Guide entry for CMN for more details on the other arguments to cmn-display.

LilyPond

On OSX with SBCL we can call lp-display to generate score PDFs via Lilypond. This itself is a wrapper for write-lp-data-for-all: after calling that method, lp-display calls Lilypond and then opens the PDF automatically. lp-display takes all the same arguments as write-lp-data-for-all. As that method works in all Lisps on all systems we will confine all of the Lilypond functionality documentation here and elsewhere to it.

The write-lp-data-for-all method produces all of the files LilyPond needs to typeset the given musical data. As with the other output methods, this method takes as its first argument the variable defined as the first argument to the make-slippery-chicken function. The write-lp-data-for-all method generates its output file names automatically based on the title slot of the given slippery-chicken object. Because of this, the method requires only the base directory path, and not the file name, if the output path is specified by the user.

NB: LilyPond can take a long time to render output, during which time it is unresponsive and may appear to have hung or frozen.

NB: See note above about Lilypond and instrument names.

(write-lp-data-for-all +primary-disposition+ :base-path "/tmp/")

See the User Guide entry for LilyPond for more details on the other arguments to write-lp-data-for-all.

As long as CMN is installed and set up properly to interact with slippery chicken, both LilyPond files and CMN output, as well as a MIDI file, can be generated at the same time.

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

;;; EOF primary-disposition.lsp

Processing the code and producing output

The code presented in this example must be processed ("evaluated") in order to generate the musical data and produce any desired output files. This can be done in a number of ways. The first way is to enter each fully-closed expression at the SC> prompt in the Lisp listener. However, this approach may be rather unwieldy for long passages of code.

The second approach assumes that the code is being written using the text-editing software Emacs and the SLIME Lisp-interaction mode. In this case, the cursor can be placed after the final parenthesis of each fully-closed expression and the key-command C-x C-e can be performed to evaluate each expression individually. More information on this approach can be found at the Emacs and SLIME websites.

The third and recommended approach is to enter and save the code into an ASCII-only text file with the suffix .lsp. It is important to be sure that the operating system is not automatically attaching and hiding a .txt or other suffix to your file. It is also important to use a text-only text editor such as WordPad or TextWrangler (or Emacs), as many text-editing applications include hidden markup commands that may prevent the code from evaluating properly.

SC> (load "/path/to/primary-disposition.lsp")

More information on loading CMN, slippery chicken, and individual slippery chicken files can be found on the installation page.

Conclusion

Depending on how slippery chicken was installed, running this code should produce at the very least a MIDI file. This is a Type 1 MIDI file (one channel per instrument), and can be imported into other sequencer or notation software.

LilyPond and CMN output will be created if the relevant code is uncommented in the downloaded file.

Should this have been done, slippery chicken should also have produced .ly files that can be open and rendered using LilyPond, resulting in a PDF of the score and/or parts.

Should CMN have been installed to interact with slippery chicken, and the corresponding code uncommented in the file, an .eps file should also have been produced. That file can be opened using Preview.app on a Macintosh (where it will be immediately converted to PDF), or, for example, Ghostscript with Ghostview or GSview on other platforms.