"Primary Disposition" — A guide to the core usage of slippery chicken
+ 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
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
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.