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 ((test-sndfile-1) (test-sndfile-2) (test-sndfile-3))) (percussive-group ((test-sndfile-4) (test-sndfile-5) (test-sndfile-6)))) ("/path/to/source-sndfiles/") ("aiff"))
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)) (get-nearest-by-freq (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)))) ("/path/to/source-sndfiles/") ("aif"))
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)))) ("/path/to/source-sndfiles/") ("aif"))
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
.
amplitude
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.
description
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 ((source-sndfile-1 :description "initial attack" :start 0.000 :end 2.308 :frequency 524 :amplitude 0.1) (source-sndfile-1 :description "snap" :start 2.274 :duration 0.1 :frequency e4 :amplitude 1.0)))) ("/path/to/source-sndfiles/") ("aif"))
+ 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.
num-sections
: This argument takes an integer that indicates the total number of consecutive sections on which the new sound file is to be based, including the first section indicated as the second mandatory argument. If the piece has sub-sections, these will be included in the total; i.e., if section 1 has 3 subsections and:num-sections
is 2, we'll generate data from the first two subsections of section 1, not all subsections of main sections 1 and 2. If this argument is set toNIL
, all sections in the piece will be used. Default =NIL
.(clm-play +sc-object+ 1 nil 'source-sndfile-grp-1 :num-sections 2)
from-sequence
andnum-sequences
: These two arguments take numbers that indicate the firstrthm-seq
and total number ofrthm-seqs
within the specified section on which the new sound file will be based. As these arguments only function within one section, thenum-sections
argument must be set to1
.(clm-play +sc-object+ 1 nil 'source-sndfile-grp-2 :num-sections 1 :from-sequence 2 :num-sequences 2)
reset-snds-each-rs
andreset-snds-each-player
: By default, slippery chicken starts at the beginning of each list of source sound files with each newrthm-seq
and each new player it processes. This behavior can be changed by setting one or both of these arguments toNIL
.(clm-play +sc-object+ 2 nil 'source-sndfile-grp-1 :reset-snds-each-rs nil :reset-snds-each-player nil)
time-scaler
: Thetime-scaler
argument takes a number that is the factor by which the durations of the segments in the new sound file will be scaled. The resulting sound file will have the same rhythmic structure as the players' parts on which it is based but at a different tempo. Use of this argument only affects durations; it does not apply pitch-shifting or time-stretching. The resulting sound file will no longer be synchronous with the events of the givenslippery-chicken
object.(clm-play +sc-object+ 2 nil 'source-sndfile-grp-2 :time-scaler 1.7)
src-scaler
: Thesrc-scaler
value will scale the playback speed of all individual sound file segments being written to the new sound file by the specified factor. This argument does not affect durations or apply time-stretching, nor does it modify the playback speed or duration of the new sound file as a whole.(clm-play +sc-object+ 3 nil 'source-sndfile-grp-1 :src-scaler 1.9)
rev-amt
: Therev-amt
value indicates how much digital reverberation is to be applied to the output file. Theclm-play
method uses thenrev
reverb instrument, for which a value of0.1
is relatively high.(clm-play +sc-object+ 3 nil 'source-sndfile-grp-2 :rev-amt 0.1)
+ 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:
channels
: The number of interleaved channels in the output file. Theclm-play
method will automatically distribute the source sound material across the channels in a manner reflecting a linear periphery of consecutively numbered loudspeakers. The number of output channels is only limited by the sound file output format used.srate
: The sample rate of the new output file.header-type:
The value given here is used to set CLM's global*clm-header-type*
variable and must be preceded by theclm::
package qualifier. Setting this toclm::mus-riff
will produce files in.wav
format;clm::mus-aiff
will produce.aiff
files.data-format
: The value given here is used to set CLM's global*clm-data-format*
variable and must be preceded by theclm::
package qualifier. Setting this toclm::mus-lfloat
will produce audio files with a format of 32-bit little-endian floating-point. Setting it toclm::mus-l24int
produces little-endian 24-bit integer files.clm::mus-bshort
will produce 16-bit big-endian files, andclm::mus-bfloat
will produce 32-bit floating-point big-endian files. NB: AIFF and AIFC files are not compatible with little endian formats.sndfile-extension
: The extension to be added to the new output file. IfNIL
, slippery chicken will automatically attach the extension that it determines to be best suited to the header type. NB: The extension does not determine the output sound file format;header-type
does that.
(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 )))