slippery chicken and CLM

+ Associated example files


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

slippery chicken's clm-play method allows the user to generate sound files based on the data structures of a slippery-chicken object. By default, the method takes one or more source sound files specified in the sndfile-palette and uses them (or segments of them) to create a new sound file based on the rhythms and certain aspects of the pitch material of a specified player or players within a given slippery-chicken object. User-defined CLM instruments can be substituted however and these may of course perform any time of synthesis or signal processing (see below)

+ Setting the output directory

The user must first designate the output path for the new sound file. This is done by setting the slippery-chicken object's snd-out-dir slot:

:snd-output-dir "/tmp/"


+ The sndfile-palette

The user must then define a sndfile-palette consisting of source sound files from which the new sound file will be created. The sndfile-palette takes a list containing at least one group, each group in turn containing a list with the name of at least one source sound file. The sound file names can be given without their file extensions if desired. If the user chooses to omit the extensions here, they must be specified in a list as the last element in the sndfile-palette (see below).

The list of groups is followed by a list of paths to the disk locations where the source sound files are stored. The source sound files can be located in any of the specified disk locations. NB Paths are not recursively searched so if some files are in subfolders, the paths to those subfolders must be in the list.

If the user has chosen to omit the file extensions from the file names, they must then be provided (minus the dot) as strings in a list as the last element of the sndfile-palette.

:sndfile-palette '(((developing-group

Only one group (or a maximum of two if a transition is to be made) can be used in each call to clm-play, but different groups can be used in separate calls to the method to generate output files with the same rhythm and pitch characteristics but different sound qualities (see the section on source sound file groups below.)

More detail can be found on the sndfile-palette class itself in its source code documentation. Each of the sound files named within the palette are used by slippery chicken to create sndfile objects. More detail on the sndfile class can be found in its robodoc page.


+ The call to clm-play

In its most straightforward form, the clm-play method takes as its arguments a slippery-chicken object (which can be passed as a local variable or as the global variable defined as the first argument to the make-slippery-chicken function), the ID of the starting section in that slippery-chicken object on which the new sound file is to be based, the IDs of the players whose events are to be used as the basis for the new sound file, and the ID of the sound file group within the sndfile-palette whose source sound files are to be used to generate the new sound file, e.g.:

(clm-play +sc-object+ 1 '(player-one player-two) 'source-sndfile-grp-1)

If instead of a list of player IDs the user sets the third argument of the clm-play method to NIL, the event data will be drawn from all players in the ensemble.

(clm-play +sc-object+ 1 nil 'source-sndfile-grp-1)


+ How it produces the new sound files

The clm-play method passes through the specified section (or sections, if more than one is indicated; see below), and for each attacked note in the parts of each of the players specified it triggers the playback of a source sound file from the specified group in the sndfile-palette.

Cycles through source sound files

The source sound files are triggered in cyclical sequence on a note-by-note, sequence-by-sequence, and player-by-player basis, following the order of the players as listed in the ensemble. Each consecutive attacked (i.e. not tied to) note in the given player's part triggers the next source sound file from the specified group in the sndfile-palette. By default, the method starts at the beginning of the list of source sound files in the specified group with each new rthm-seq, triggering them in sequence each time, and cycling back to the beginning again if all source sound files have been triggered before the end of the given rthm-seq. Once all of the notes have been processed for one player, the method moves to the next player.

Update to slippery chicken version 1.0.6

As detailed above, by default the sound files in the given group are cycled through, one after the other, returning to the beginning when the end is reached. There is no fundamental change in 1.0.6, but the following functionality has been added: The circular selection can be overridden by passing a function to clm-play via the keyword :snd-selector. This function must take three arguments: the circular-sclist object that contains the group's sndfile objects (the sound files are in a simple list in the data slot); the pitch of the current event (if this happens to be a chord, then each pitch of the chord will be handled separately i.e. the :snd-selector function will be called for each pitch); and the event object. Of course any of these arguments may be ignored by the :snd-selector function; indeed it is imagined that the event object will be mostly ignored in favour of the pitch object, but it is nevertheless passed for completeness. For example, the following function--declared on-the-fly using lambda rather than separately with defun--makes clm-play act as a traditional sampler would: Assuming our sndfile-palette group has a list of sound files whose frequencies correspond to their perceived fundamentals (e.g. see the set-frequency-from-filename method, and the kontakt-to-sfp function) we choose the nearest sound file in the group to the frequency of the current event's pitch:

           :snd-selector #'(lambda (sflist pitch event)
                             (declare (ignore event))
                              (frequency pitch) (data sflist)))

Omits initial rests

The clm-play method begins its output with the first attacked note in the specified sequence. Thus, if the first event of the given sequence is an attacked note, the new sound file will be exactly rhythmically synchronous with the parts of the specified players. This is especially audible if the source sound files are percussive in nature. (This synchronicity can be obscured, if desired, by methods such as setting the inc-start etc., as described below.)

If the specified sequence begins with one or more rests, the method will ignore those rests, and the new sound file will begin with the first attacked event at 0.0 seconds. The resulting sound file in such cases will therefore be rhythmically synchronous to the given players' parts, but with a minor offset at the beginning.

Simple playback

The clm-play method does not process the source sound files it is given by any means other than shifting their pitch, assembling a sequence of sound files or file fragments for playback, and distributing the individual sound files or fragments amongst the channels for spatialized playback. There is no synthesis or filtering involved.


+ Start times, end times, and durations

Unless otherwise indicated, the clm-play method triggers the source sound files from their beginning and continues playback of the same source sound file until the next attacked note in the given player's part, ignoring rests. By default, if the given source sound file is longer than the duration between two attacked notes, playback of the source sound file will be interrupted at the next attacked note. If the source sound file is shorter than the duration between two attacked notes, it will play out in full.

Three optional keyword arguments are available to the clm-play method to modify this default behavior, namely :inc-start, :duration-scaler, and :ignore-rests. Three additional keyword arguments can be specified within the sndfile-palette that have a related effect, namely :start, :end, and :duration.

clm-play arguments for start times, end times, and durations

If the :inc-start argument is set to T, the clm-play method will automatically start playback of each source sound file from an increasingly later point in that file each time the file is triggered. The increment interval is auto-calculated, so that it reaches the end of the input sound file by the last time it is triggered. The :inc-start argument is set to NIL by default.

The :duration-scaler argument causes clm-play to scale the duration of each source sound file segment by a specified factor. If the argument is given a value less than 1.0, the segments played back will end before the next attacked note, and if it is set to a value greater than 1.0, the playback of the segments will not end until after the next attacked note (should they be long enough), causing the segments to overlap. Overlapping segments will only be generated for events within the same sequence, but will not overlap from one sequence to the next. The :duration-scaler argument only affects the duration of the segment played back; it does not alter pitch or implement time-stretching. This argument is set to 1.0 by default.

If the :ignore-rests argument is set to NIL, the clm-play method will mute playback of the source sound file segments at points that correspond with rests in the given player's part. This argument is set to T by default. Similar to the :duration-scaler, ignoring rests only has an effect within the same sequence. All sounds will end at the end of a sequence, even if the next sequence begins with rests.

(clm-play +sc-object+ 1 'player-one 'source-sndfile-grp-1 
          :inc-start t
          :duration-scaler 1.3
          :ignore-rests nil)

Specifying start and end times within the sndfile-palette

The user can indicate that only a specified segment of a given source sound file is to be played back using the :start and :end or :duration keyword arguments within the sndfile-palette. These arguments correspond to the identically named slots of the sndfile class. Each of these arguments takes either a single numerical value consisting of time in seconds, or a list of numbers indicating (mins secs millisecs).

The same source sound file can be listed in the sndfile-palette multiple times with different :start and :end or :duration times (or with the same values to get a repeat). This has the same effect as listing multiple separate source sound files, as the clm-play method will cycle through these in the same manner by which it cycles through separate sound files.

The :duration argument automatically determines the end time of the given segment by adding the specified number of seconds to the start time. The user should apply either the :end or :duration argument to a given source sound file, but not both.

If a :start time is indicated with no :end or :duration time, the end time will default to the end of the source sound file. Correspondingly, if only an :end or the :duration time is specified, the segment will begin playback at the beginning of the source sound file.

:sndfile-palette '(((sustained-pads-group
                     ((source-sndfile-1 :start 0.000 :end 2.308)
                      (source-sndfile-1 :start 2.274 :duration 0.1)
                      (source-sndfile-1 :start (0 4 748))
                      (source-sndfile-1 :end 7.255)
                      (source-sndfile-1 :duration 2.1))))

NB: The :duration argument within the sndfile-palette is only used to demarcate a segment within a source sound file; the actual duration played back will be decided by the rhythms of the corresponding player part and the values of the :duration-scaler and :ignore-rests arguments.


+ Base frequency

slippery chicken has two options for influencing the speed (transposition via sample-rate conversion) at which it plays back the various source sound files. Both of these options make use of the frequency slot when determining these speeds.

Some sounds have a prominent fundamental pitch that can be used as a reference for transposition. Setting the frequency slot to this fundamental pitch, either in the form of a number in Hertz or a note-name symbol, will determine the sample-rate conversion factor when transposing. If no pitch is specified for the frequency slot, the value defaults to C4.

The frequency value can be defined for each source sound file within the sndfile-palette.

:sndfile-palette '(((percussive-models-group
                     ((source-sndfile-1 :start 0.000 :end 2.308)
                      (source-sndfile-1 :start 2.274 :duration 0.1 
                                                     :frequency 524)
                      (source-sndfile-1 :start 4.748 :frequency c3)
                      (source-sndfile-1 :end 7.255 :frequency c5)
                      (source-sndfile-1 :duration 2.1 frequency d4))))

Non-pitch-synchronous transposition

The first option available to the user regarding transposition, which is slippery chicken's default behavior, is to determine the sample-rate conversion based on the pitches of the current set. In this option, slippery chicken will not attempt to match the transpositions of the source sound files to the exact pitches in the parts of the given players. It will still, however, use the :note-number argument to access chord notes from the bottom up.

Pitch-synchronous transposition

In the second option, slippery chicken scales the playback speed of the source sound files to match the specific pitches of the given players' parts. The pitches in the resulting output file will only match those of the players' parts if the source sound file has a perceptible fundamental pitch and this has been defined accurately within the sndfile-palette using the frequency slot. This option will also result in chords from the players' parts being reflected in the sound file output.

This second option will only be applied if the :pitch-synchronous argument of the clm-play method is set to T.

(clm-play +sc-object+ 1 'flt 'source-sndfile-grp-1
          :pitch-synchronous t)

Specifying a frequency for sounds with no fundamental

The frequency slot can also be an effective tool for source sound files with no perceptible fundamental pitch. In general, setting a higher frequency value will cause the source sound file to be played back at a lower pitch, while setting it lower will cause the source sound file to play back higher.

Both pitch and duration are affected

Transposition by sample-rate conversion is a time-domain process, so as with changing the speed of a tape recorder or turntable, both pitch and timing of events is altered.


+ Source sound file groups

The sndfile-palette definition can consist of multiple groups of source sound files. As described above, the clm-play method is designed to only utilize one of these groups for any given output file, with the exception of the fibonacci-transition option described in the next section. The approach of calling the clm-play method several times, specifying a different source sound file group for each call, is demonstrated here.

(clm-play +sc-object+ 1 '(player-one player-two) 'source-sndfile-grp-1
          :duration-scaler 0.7
          :inc-start nil)
(clm-play +sc-object+ 1 '(player-one player-two) 'source-sndfile-grp-2
          :duration-scaler 1.3
          :inc-start t)
(clm-play +sc-object+ 1 '(player-three) 'source-sndfile-grp-3
          :ignore-rests nil)

The resulting sound files can then be combined and mixed using a third-party digital audio workstation, such as Logic, Cubase, or the open-source alternative Ardour.

Fibonacci-transitioning between two sound file groups

The one native option that slippery chicken has available for incorporating more than sound file group into the same output file consists of a fibonacci-transition between two groups in the same sndfile-palette. This is done by specifying the first group as usual and the second group as the value of the optional keyword argument :sound-file-palette-ref2.

(clm-play +sc-object+ 1 '(player-one player-two) 'source-sndfile-grp-1  
          :sound-file-palette-ref2 'source-sndfile-grp-2)


+ Other useful sndfile class slots

Two other useful slots of the sndfile class that can be given values within the sndfile-palette are amplitude and description.


The amplitude slot sets the relative amplitude of the given source sound files (or segments of the same source file if using :start and :end or :duration). Since clm-play by default instructs CLM to normalize the new sound file to just below the highest sample value, this argument can take a number of any arbitrary value, and the amplitudes will be scaled accordingly.


The description slot allows the user to add a comment about the given source sound file or segment defined. This value is not used for any purposes other than information.

:sndfile-palette '(((percussive-models-group
                       :description "initial attack"
                       :start 0.000 :end 2.308 :frequency 524 
                       :amplitude 0.1) 
                       :description "snap"
                       :start 2.274 :duration 0.1 :frequency e4 
                       :amplitude 1.0))))


+ Other useful clm-play arguments

The clm-play method takes 4 mandatory arguments and has 36 optional keyword arguments. A description of each of these arguments can be found in the source code documentation for clm-play. A number of the more frequently used optional keyword arguments are given here in brief.


+ clm-play arguments for output format

The clm-play method produces stereo sound files by default, with the header type, data format, and sample rate that are stored in CLM's global *clm-header-type*, *clm-data-format*, and *clm-srate* variables. CLM's default data format and sample rate are 16 bit signed integer and 44100 kHz. The default header-type is dependent on the user's operating system. slippery chicken will automatically attach the sound file extension it determines to be best suited to the header type, though this parameter too can be specified by the user.

These attributes can be specified by providing values for the following keyword arguments:

(clm-play +sc-object+ 1 nil 'source-sndfile-grp-1 
          :channels 8
          :srate 44100
          :header-type clm::mus-aiff
          :data-format clm::mus-bshort
          :sndfile-extension ".aiff")


+ Tape parts with independent rhythmic structures

Since clm-play creates its output files based on the events of one or more players in the ensemble, if the user wishes to create an output file that is not synchronous in rhythmic structure to any of the instrumental parts, or if the user would like to create an electroacoustic composition with no instrumental players, "silent" computer-part players can be added to the ensemble. A corresponding computer instrument exists for this purpose in the +slippery-chicken-standard-instrument-palette+.

Specific rthm-seq IDs can then be given for these players in the rthm-seq-map, and these "silent" players can be passed as the player argument to the clm-play method to generate the desired tape part.

As the computer instrument is defined to encompass the full MIDI pitch range (C-1 to G9), the use of set-limits-high and -low may be recommended for limiting transposition registers.

Adding a computer part to the ensemble:

:ensemble '(((fl (flute :midi-channel 1))
             (ob (oboe :midi-channel 2))
             (cl (b-flat-clarinet :midi-channel 3))
             (cp (computer))))

Limiting transposition register:

:set-limits-high '((cp (0 c6 100 c6)))
:set-limits-low '((cp (0 f3 100 f3)))

Defining a separate rhythmic structure for the computer part within the rthm-seq-map:

:rthm-seq-map '((1 ((fl (1 2 3 1 3 2))
                    (ob (2 3 1 3 2 1))
                    (cl (3 1 3 2 1 2))
                    (cp (1 3 2 1 2 3)))))

Calling clm-play for just the rhtm-seq-map data of the cp part:

(clm-play +sc-object+ 1 'cp 'source-sndfile-grp-1)

Excluding computer players from the score and MIDI

If this approach is chosen, the "silent" players of the ensemble can then be excluded from the MIDI output by specifying voices to the midi-play method, and excluded from printable score output by calling cmn-display or write-lp-data-for-all with the players argument.

Specifying only a selection of the players for the midi-play method (excluding the computer player):

(midi-play +sc-object+ :voices '(fl ob cl) :midi-file "/tmp/output.mid") 

Specifying only a selection of the players for the cmn-display and write-lp-data-for-all methods (excluding the computer player):

(cmn-display +sc-object+ :players '(fl ob cl) :file "/tmp/output.eps")

(write-lp-data-for-all +sc-object+ :players '(fl ob cl) :base-path "/tmp/")


+ User-defined CLM instruments with clm-play

Instead of using the default CLM samp5 instrument, the user can have their own instrument (or slippery chicken's simple sine instrument) called for them by clm-play by providing the instrument name as a function to clm-play's :clm-ins keyword argument. (Remember that your CLM instrument will most probably be in the CLM package so you'll need the clm:: package qualifer, e.g. #'clm::my-clm-instrument.)

The instrument passed must accept all the required and keyword arguments of samp5 (see src/samp5.lsp or more simply src/sine.lsp for a list of these) but can of course choose to ignore them. Any further arguments you would like to pass to your instrument can be given in :clm-ins-args. This should be a list of keyword arguments and values to pass to each CLM instrument call e.g. '(:cutoff-freq 2000 :q .6) or a function which takes two arguments (the current event and the event number) and returns a list of keyword arguments and their values, perhaps based on the event data.

(clm-play mini 1 nil nil :num-sections 3 :play nil :check-overwrite nil
          :clm-ins #'clm::sine
          :clm-ins-args '(:unused-arg-for-testing t))

(clm-play mini 2 nil nil :num-sections 3 :play nil :check-overwrite nil
          :clm-ins #'clm::sine
          :clm-ins-args #'(lambda (event event-num)
                            (print-simple event) ; current event object
                            (print event-num)    ; current event number
                            (list :my-ins-keyword-arg1 'foo
                                  :my-ins-keyword-arg2 'bar )))