Adding a tape part using CLM

+ Associated example files

close


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

One of the fundamental goals in the development of slippery chicken was to provide a means of unifying instrumental and electroacoustic sound worlds in a convincing manner for compositions that make use of both resources. This brief tutorial will provide a quick introduction into how to produce a tape part from a slippery-chicken object using clm-play. A more detailed description of this topic, with instruction on how to generate independent tape parts, can be found in the Tempus Perfectum tutorial.

This brief demonstration will use the same code used in the Second Law tutorial, but will expand it through the addition of a sndfile-palette and two new calls to the output method clm-play, thereby creating two tape parts from the musical data already present in the instrumental parts.

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

(let* ((sndfiles-dir-1 
        (concatenate 'string 
                     cl-user::+slippery-chicken-home-dir+ 
                     "doc/manual/resources/"))
       (sl-rsp-orig ; defining the source material
        (make-rsp
         'sl-rsp
         '((1 
            ((((4 4) - (e.) s - - \+32 32 32 32 (e) - 
               - 32 32 (s) 32 32 32 (32) - - (s) s s (s) - )
              (- (e..) 32 - +q q (q))
              (h. (q)))
             :pitch-seq-palette ((1 2 1 2 3 2 3 3 1 2 5 5 7 6)
                                 (2 1 2 1 2 3 2 2 4 3 2 2 1 2)
                                 (5 3 4 5 4 5 4 5 2 3 6 6 8 7))
             :marks (a 1 s 1 slur 3 5 a 6 slur 6 7 slur 8 10 a 11 s 11 12 
                       a 13))))))
       (sl-rsp-chopped (chop sl-rsp-orig 
                             '((1 2) (1 1) (2 2)) ; chop points
                             'e)) ; chopping unit
       (num-seqs-list '(53 61 97 79 89 73)))
  (loop for i in 
       '((flute 13)
         (oboe 7)
         (b-flat-clarinet 9)
         (bassoon 7)
         (french-horn 5)
         (b-flat-trumpet 7)
         (tenor-trombone 5)
         (double-bass 5))
     do
       (set-slot 'largest-fast-leap 
                 (second i)
                 (first i)
                 +slippery-chicken-standard-instrument-palette+))
  (make-slippery-chicken
   '+second-law+
   :title "Second Law"
   :instrument-palette +slippery-chicken-standard-instrument-palette+
   :ensemble '(((fl (flute :midi-channel 1))
                (ob (oboe :midi-channel 2))
                (cl (b-flat-clarinet :midi-channel 3))
                (bn (bassoon :midi-channel 4))
                (hn (french-horn :midi-channel 5))
                (tp (b-flat-trumpet :midi-channel 6))
                (tb (tenor-trombone :midi-channel 7))
                (vno (violin :midi-channel 8))
                (vnt (violin :midi-channel 9))
                (va (viola :midi-channel 12))
                (vc (cello :midi-channel 13))
                (cb (double-bass :midi-channel 14))))
   :set-limits-high '((cl (0 c6 100 c6))
                      (vc (0 a4 100 a4))
                      (cb (0 f3 100 f3)))
   :staff-groupings '(4 3 5)
   :tempo-map '((1 (q 69)))
   :set-palette '((1 ((c3 g3 cs4 e4 fs4 a4 bf4 c5 d5 f5 gf5 af5 ef6)))
                  (2 ((c3 fs3 cs4 e4 g4 a4 b4 c5 df5 f5 g5 af5 ef6)))
                  (3 ((d3 f3 cs4 e4 fs4 a4 b4 c5 d5 e5 fs5 af5 ef6)))
                  (4 ((d3 e3 cs4 ef4 fs4 a4 b4 c5 d5 e5 fs5 af5 d6)))
                  (5 ((d3 e3 g3 a3 c4 ef4 f4 af4 bf4 cs5 fs5 b5 df6)))
                  (6 ((c3 d3 gf3 af3 b3 e4 a4 df5 ef5 g5 bf5 df6)))
                  (7 ((b2 e3 fs3 as3 ef4 g4 a4 d5 f5 af5 c6 df6)))
                  (8 ((af2 b2 ef3 fs3 as3 cs4 e4 g4 a4 d5 f5 bf5 c6 e6 af6))) 
                  (9 ((af2 b2 ef3 fs3 bf3 d4 f4 a4 cs5 e5 g5 c6 f5 af6)))
                  (10 ((af2 c3 ef3 fs3 bf4 d4 f4 a4 cs5 e5 g5 b5 fs6))))
   :set-map (loop for section in 
                 '((1 (1 2 3)) 
                   (2 (2 3 4 1))
                   (3 (1 3 5 6 7))
                   (4 (8 9))
                   (5 (5 6 7 9 3))
                   (6 (9 10)))
               collect
                 (list (first section)
                       (fibonacci-transitions 
                        (nth (1- (first section)) num-seqs-list)
                        (second section))))
   :rthm-seq-map (loop for section in
                      '((((1 3) fl ob ))
                        (((3 4) fl ob cl))
                        (((5 6 7 8) fl ob cl)
                         ((11 12 13 14) bn tb vc cb))
                        (((9 10 11) hn tp))
                        (((15 16 25 26) fl ob vno vnt)
                         ((9 10 13 14) cl hn va)
                         ((3 1 16 3) tp tb)
                         ((12 13 10 11) bn vc cb))
                        (((1 3 4) fl ob cl bn hn tp tb vno vnt va vc cb)))
                    for section-num from 1
                    collect 
                      (list section-num
                            (loop for ins-group in section 
                               appending
                                 (loop with fts =
                                      (loop for ch in (first ins-group) 
                                         collect
                                           (list 1 ch))
                                    for ins in (rest ins-group) 
                                    collect
                                      (list ins
                                            (fibonacci-transitions
                                             (nth (1- section-num)
                                                  num-seqs-list) 
                                             fts))))))
   :snd-output-dir "/tmp/"
   :sndfile-palette `(((source-sndfile-grp-1
                        ((test-sndfile-2.aiff :start 0.000 :end 0.504)
                         (test-sndfile-2.aiff :start 0.504 :end 0.884)
                         (test-sndfile-2.aiff :start 0.884 :end 1.608)))
                       (source-sndfile-grp-2
                        ((test-sndfile-3.aiff 
                          :start 0.035 :end 0.426 :frequency 664)
                         (test-sndfile-3.aiff 
                          :start 0.426 :end 0.682 :frequency 664)
                         (test-sndfile-3.aiff 
                          :start 0.682 :end 2.200 :frequency 664))))
                      ,(list sndfiles-dir-1))))

(re-bar +second-law+ :min-time-sig '(4 4) :auto-beam 'q)

;;; the output

;;; midi
(midi-play +second-law+ :midi-file "/tmp/second-law.mid")

;; CMN
(cmn-display +second-law+ :file "/tmp/second-law.eps" :size 12)

;; LP
(write-lp-data-for-all +second-law+ :base-path "/tmp/")

;; CLM
(clm-play +second-law+ 1 nil 'source-sndfile-grp-1
          :rev-amt 0.1
          :src-width 5)

(clm-play +second-law+ 1 nil'source-sndfile-grp-2
          :reset-snds-each-rs nil
          :reset-snds-each-player nil
          :rev-amt 0.1
          :src-width 5)

close

+ The basic concept behind clm-play

slippery chicken's clm-play method functions by generating sound files from the rhythm and pitch data in one or more players' parts. In short, each attacked note in the specified part(s) will trigger the playback of a sound file, and the playback speed of this sound file will be adjusted on the basis of one of the pitches of the current pitch set. The result is a tape part that is directly created from the same compositional building blocks as the instrumental parts, producing a piece whose electroacoustic and instrumental sound worlds are unified by pitch, rhythm, and structure.

The clm-play method has 36 optional arguments, providing the user with a broad number of specific parameters when shaping the sound file output. This example will only make use of four of these arguments. Further, more detailed information on the remaining arguments can be found in the tutorial for Tempus Perfectum, the manual page for slippery chicken and CLM, and the source code documentation.

close

+ Defining the palette of source sound files

The first requirement for producing such sound files is the definition of a sndfile-palette. This is where the sound files that will serve as the source samples for the resulting output file are identified.

   :snd-output-dir "/tmp/"
   :sndfile-palette `(((source-sndfile-grp-1
                        ((test-sndfile-2.aiff :start 0.000 :end 0.504)
                         (test-sndfile-2.aiff :start 0.504 :end 0.884)
                         (test-sndfile-2.aiff :start 0.884 :end 1.608)))
                       (source-sndfile-grp-2
                        ((test-sndfile-3.aiff 
                          :start 0.035 :end 0.426 :frequency 664)
                         (test-sndfile-3.aiff 
                          :start 0.426 :end 0.682 :frequency 664)
                         (test-sndfile-3.aiff 
                          :start 0.682 :end 2.200 :frequency 664))))
                      ,(list sndfiles-dir-1))

Groups

The source sound files (test-sndfile-1.aiff, test-sndfile-2.aiff, and test-sndfile-3.aiff) are collected into groups with user-defined IDs. These groups consist of each separate sound file contained in a list that may or may not specify further parameters, such as the start and end point within the source sound file from which to take the sample, and the perceived fundamental frequency of that sound file or sound file segment. There can be as many groups as the user likes, and each group may contain as many sound files as the user likes as well. If multiple segments of the same sound file are specified, these will be treated by clm-play as though they were separate sound files.

File type extensions

The names of the sound files can be stated with or without the file extension. If the extension is omitted, it must be added as a string within a list that forms the third element of the sndfile-palette. This example specifies the extensions when naming the files within the groups. See the other pages mentioned above for more on the syntax that allows the extensions to be omitted within the groups.

Paths

Following the list of groups, the sndfile-palette also requires a list of directory paths where the source sound files are located. This example assigns the path where the desired source sound files are stored to the variable sndfiles-dir-1 at the top of the let* enclosure, and passes it to the sndfile-palette using Lisp's backquote and comma syntax. The path itself is created by concatenating the global slippery chicken parameter +slippery-chicken-home-dir+ with the path to the resources directory of the sc/doc/manual/ folder.

       (sndfiles-dir-1 
        (concatenate 'string 
                     cl-user::+slippery-chicken-home-dir+ 
                     "doc/manual/resources/"))

close

+ The basic call to clm-play

The clm-play method can produce output with just its four mandatory arguments, namely the slippery-chicken object whose data is to be used to generate the new sound file, the ID of the first section within that slippery-chicken object on which the output is to be based, the IDs of the players whose parts are to provide the pitch and rhythm material for the new sound file, and the ID of the group within the sndfile-palette whose source sound files are to be used to create the output.

This example creates two new audio files based on the musical data from the piece Second Law. Each call to clm-play produces one new sound file. The first four arguments of the two calls to clm-play in this demo composition are as follows:

(clm-play +second-law+ 1 nil 'source-sndfile-grp-1 [...]

(clm-play +second-law+ 1 nil 'source-sndfile-grp-2 [...]

Both calls will create new output sound files based on the pitch and rhythm data of the slippery-chicken object with the global ID +second-law+, and both will create their output starting with section 1 of the piece defined in that object.

Instead of specifying a list of player IDs as the third argument, this example passes NIL. Passing NIL as the third argument causes the method to use all event objects (pitches and rhythms) from all players' parts to create the resulting sound file output.

The fourth argument of the first call here to clm-play specifies that the source sound files from the first group defined in the sndfile-palette, with the ID 'source-sndfile-grp-1, are to be used to generate the first output file. The second sound file will then be generated by the second call to clm-play using the source sound files of the second group.

close

+ The four optional arguments used here

This example makes use of four of the 36 optional arguments available to the clm-play method. These are :rev-amt, :src-width, :reset-snds-each-rs, and :reset-snds-each-player.

:rev-amt

The :rev-amt argument determines how much reverberation will be added to the resulting sound file. If this argument is not given a specific value by the user, the amount of reverb defaults to 0. The value of 0.1 used in both calls to clm-play here is already a relatively high amount of reverb.

:src-width

The value passed to the :src-width argument determines the depth of the interpolation algorithms used when transposing source sound files to other playback speeds ("sample-rate conversion" = "src"). The higher the value, the finer the quality of the transposition, but the longer it will take to process.

This value defaults to 20 if not otherwise specified. It is set to 5 in each of the calls to clm-play here to facilitate faster rendering of the resulting sound files. A value this low is highly recommended when producing test sound-files of longer segments.

:reset-snds-each-rs and :reset-snds-each-player

These two related arguments affect the cyclical selection of the sound files from each group. The clm-play method cycles through the list of source sound files of the given group from event to event in the players' parts. When the last sound file in that list has been used, the method returns to the first one again. By default, the method will "reset" these sounds at each sequence and with each new player; i.e., each consecutive section will always start with the first sound in the list.

The second call to clm-play in this example here sets both of these arguments to NIL. Doing so causes the method to continue selecting the subsequent sounds from the given group consecutively even past the beginning of a new sequence, producing a slightly less obvious demarcation of the sequences within the new output sound file. The first call to clm-play in this example leaves these arguments at their default value of T for a slightly different structural quality in its output file.

close

+ Putting it all together with external software

Since this example uses multiple calls to clm-play, and thereby produces more than one new sound file, the final stereo tape part had to be created using an external digital audio workstation. In this case, the two output .aiff files were imported into Digital Performer, mixed together with a sample-based MIDI mock-up of the instrumental parts, and bounced to disk as an MP3, which can be accessed from the corresponding link in the "Associated example files" section above.

close