Rhythm Chains

+ Associated example files

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

The `rthm-chain` class provides a means of algorithmically generating two-voice `rthm-seq-palettes` and `rthm-seq-maps` that can be inserted directly into a `slippery-chicken` object. It creates the two voices by automatically assembling sequences of user-defined rhythmic fragments of either one, two, or three beats.

One of the class's more versatile features is its diversification of the user-defined source material through the automatic insertion of rests and the automatic repetition of what it calls sticking rhythms at certain points in the new material. Both of these features can be influenced by the user through the specification of patterns for the insertion.

The class also offers the user arguments that control the level of activity in the two voices (more notes vs. more rests) by means of envelopes.

+ The "procession" algorithm

The primary sequence-generating function at the core of the `rthm-chain` class is the `procession` algorithm. This function is used by default to internally determine the resulting order of the user-defined fragments in the resulting palettes and maps by extrapolating a list of a specified length from items of a specified starting list.

Using procession with a list

The `procession` algorithm is purely deterministic; i.e., it incorporates no randomness. It starts with the first three elements of the initial list and gradually adds successive elements from that list until all of the elements have been added. The initial list must therefore have at least four elements.

```(procession 73 '(a b c d e))

=>
(A B A B C A C A A D B C B D B C B C D A D A A E C D C E B D B D E C D C C
E A B A E B D B D E A C A A E B D B E C D C D E A B A A E C D C E B D B)```

Using procession with an integer

Instead of passing a list to the function as its second argument, the user can instead pass an integer. In this case, the function will generate its output from a list of consecutive integers from `1` to the integer passed as the second argument.

```(procession 73 4)

=>
(1 2 1 2 3 1 3 1 1 4 2 3 2 4 2 3 2 3 4 1 3 1 1 4 2 3 2 4 1 3 1 3 4 2 3 2 2
4 1 3 1 4 2 3 2 3 4 1 2 1 1 4 2 3 2 4 1 3 1 3 4 2 3 2 2 4 1 3 1 4 1 3 1)```

close

+ The procession keyword arguments

The `procession` function has three optional arguments that contribute to determining the sequence that is generated, namely `:peak`, `:expt`, and `:orders`.

:peak

The value passed to the `:peak` argument has an influence on the position at which the last item of the original list first appears in the new list. It takes a floating-point number between `0.0` and `1.0` as its value, representing a span of 0% to 100% of the way through the new list, and it defaults to `0.7`.

It is important to note that this value is an approximate value only. Shorter initial lists will reflect this percentage value less accurately, and the position of the final item in the resulting list will also be dependent on other factors, such as the values of the `:orders` argument discussed below.

:expt

The value passed to the `:expt` argument determines the curve of the interval at which each subsequent element of the original list is added to the new list. This too is an approximate argument, and its accuracy will again be influenced by aspects such as the lengths of the original and resulting lists.

In general, the higher this number is, the steeper the exponential curve will be. A steeper curve means that subsequent elements of the original list will be introduced less frequently towards the beginning of the new list, and then more frequently towards the end. This argument also takes a floating-point number, and it defaults to `1.3`.

:orders

This argument takes a list of sublists that represent the patterns by which elements from the original list are introduced to the list returned by the `procession` function.

Each consists of all of and only the numbers `1`, `2`, and `3`. These numbers represent the three least used elements in the new list at each pass through the function's list-generating loop. The patterns are cyclically applied and default to `'((1 2 1 2 3) (1 2 1 1 3) (1 2 1 3))`.

The following is an example of the use of the `procession` algorithm using all three of these keywords:

```(procession 500 '(a b c d e f g h i j k l m n o p q r s t u v w x y z)
:peak 0.9
:expt 0.7
:orders '((1 3 2 2 3 1) (3 1 1 2 3 3) (1 2 1 3 2 2)))

=> (A C B B C A E A A D E E B D B F D D C F E E F C G A A F G G B C B G C C D H
F F H D H E E G H H G H G I H H A I B B I A J D D I J J F I F J I I C J E E
J C K G G J K K I J I K J J A K B B K A L D D K L L F K F L K K C L H H L C
M E E L M M G L G M L L A M I I M A N B B M N N D M D N M M F N H H N F O J
J N O O K N K O N N L O M M O L P N N O P P C O C P O O E P O O P E P G G I
P P A P A Q P P B Q D D Q B Q F F H Q Q I J I Q J J K R Q Q R K R L L Q R R
M Q M R Q Q N S R R S N S O O R S S P R P S R R C S R R S C T E E S T T G S
G T S S H T S S T H T A A Q T T B T B U T T D U T T U D U F F I U U K L K U
L L M V U U V M V N N U V V O U O V U U P V U U V P W Q Q V W W R V R W V V
S W V V W S W T T V W W C E C W E E G X W W X G X H H W X X I W I X W W J X
U U X J Y V V X Y Y A X A Y X X B Y X X Y B Y D D X Y Y F X F Y X X K Z Y Y
Z K Z M M Y Z Z N Y N Z Y Y O Z P P Z O Z Q Q R Z Z S T S Z T T W Z Y Y Z W
Z C C G Z Z H I H J I I L U R R U L Z A A V Z Z B D B E D D F J G G J F N K
K M N N O P)```

close

+ General attributes of the make-rthm-chain function

A rthm-seq-map and a rthm-seq-palette

The user can create a new object of the `rthm-chain` class using the function `make-rthm-chain`. The `rthm-chain` class is a subclass of the `rthm-seq-map` class, one of whose slots is designed to contain a `rthm-seq-palette`. The `make-rthm-chain` function therefore creates an object that defines a `rthm-seq-map` and automatically generates an accompanying `rthm-seq-palette` for that map. This palette can be accessed using `(palette rthm-chain-object)`.

The map itself and the contents of the map's `palette` slot can both be passed directly to the `:rthm-seq-map` and `:rthm-seq-palette` arguments of the `make-slippery-chicken` function, as described below.

Internal implementation of the procession algorithm

By default, the `make-rthm-chain` function will use the `procession` algorithm to generate its sequences. The user must therefore define at least four 1-beat and four 2- and 3-beat rhythm fragments within the corresponding arguments of the function call (see below). Due to the way these rhythms are processed and assembled, the rhythm fragments defined must not contain any individual rhythms that overlap beats.

Keyword arguments

The `make-rthm-chain` function has 17 optional keyword arguments. These influence traits such as the activity levels of the two voices (rests vs. notes), the automatic rest insertion, and the automatic insertion of "sticking rhythms", as described below.

close

+ Explaining make-rthm-chain through a simple example

The following simple example demonstrates the argument requirements, syntax, and usage of the `make-rthm-chain` function.

```(let* ((rch
(make-rthm-chain
'test-rch 143
'((((e) e) ; 4 in total
(- s (s) (s) s -)
({ 3 (te) - te te - })
((e.) s))
(({ 3 (te) te (te) }) ; what we transition to
({ 3 - te (te) te - })
({ 3 (te) - te te - })
({ 3 (te) (te) te }))
(((q)) ; the second transition
(- s e s -)
({ 3 te (tq) })
(s (e.)))
((- e e -) ; the third transition
(- s s s - (s))
((32) 32 (e.))
((q))))
'((((q q) ; the 2/4 bars: 4 total
((q) q)
((q) q)
((q) (s) e.))
(({ 3 te+te te+te te+te }) ; what we transition to
(q - s e. -)
(q (s) e.)
(q (s) - s e -))
((q - e e -) ; the second transition
((e) q.)
(q - s (e) s -)
((s) q e.)))
((((e.) s (e) e (s) e.) ; the 3/4 bars: 4 total
(- e e - (e) e (q))
(- e. s - - +e e - (q))
(q (e.) s (q)))
(({ 3 (te) (te) te+te te+te } (q)) ; what we transition to
(- e. s - (q) (s) - s e -)
({ 3 te+te te } (q) q)
({ 3 - te te te - } (e) e { 3 (te) (te) te }))
((h.) ; the second transition
(h (q))
(- e e - +q (q))
(s (e.) (h)))))
:players '(fl cl)))
(mini
(progn
(create-psps (palette rch))
(make-slippery-chicken
'+mini+
:ensemble '(((fl (flute :midi-channel 1))
(cl (b-flat-clarinet :midi-channel 2))))
:set-palette '((1 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6))))
:set-map `((1 ,(ml 1 (num-rthm-seqs rch))))
:tempo-map '((1 (q 120)))
:rthm-seq-palette (palette rch)
:rthm-seq-map rch))))
(midi-play mini)
(cmn-display mini)
(write-lp-data-for-all mini))```

The first two arguments passed to the `make-rthm-chain` function consist of a user-defined ID for the `rthm-chain` object being created and an integer that determines the length of the resulting musical passage, in the form of the number of bars, that the function is to generate. Remember that these bars will only be one, two, or three beats long.

The user-defined source rhythm fragments

1-beat fragments

The next two arguments are the lists of source fragments defined by the user. The first list must consist of 1-beat fragments, and there must be at least four of these. These rhythms will be used to create the first voice of the resulting object, and can be thought of as the fast-moving voice.

```         '((((e) e) ; 4 in total
(- s (s) (s) s -)
({ 3 (te) - te te - })
((e.) s)))```
2- and 3-beat fragments

The next argument to the `make-rthm-chain` function takes a list of two sublists. The first of these sublists takes at least four 2-beat rhythmic fragments, and the second takes at least four 3-beat rhythmic fragments.

These fragments will form the second voice of the resulting object, and can be thought of as the slower-moving counterpoint (though the user can, of course, also fill these lists with fast rhythms if preferred). Remember that the rhythms defined here must not overlap beats. If longer durations are desired, the beats should be divided up and connected using ties.

```         '((((q q) ; the 2/4 bars: 4 total
((q) q)
((q) q)
((q) (s) e.)))
((((e.) s (e) e (s) e.) ; the 3/4 bars: 4 total
(- e e - (e) e (q))
(- e. s - - +e e - (q))
(q (e.) s (q)))))```

player IDs

Although the `:players` argument is technically optional, the user will generally want to specify the player IDs in the call to `make-rthm-chain` for best results. These IDs must be the same as those specified in the `ensemble` of the `slippery-chicken` object in which this `rthm-chain` object will be used.

`         :players '(fl cl))))`
```  (let* ((mini
(make-slippery-chicken
'+mini+
:ensemble '(((fl (flute :midi-channel 1))
(cl (b-flat-clarinet :midi-channel 2))))```

Automatic pitch-seqs

The object created by `make-rthm-chain` will contain a `rthm-seq-palette` with individual `rthm-seq` objects, but the `pitch-seq-palettes` of those `rthm-seqs` will be initially empty. A convenient and quick way of creating `pitch-seqs` for the pitchless rhythms in this, or indeed any, `rthm-seq-palette` is the `create-psps` method.

The `create-psps` method must be called separately, after the creation of the `rthm-seq-palette` object for which it is to create `pitch-seqs`. It takes one argument, namely the `rthm-seq-palette` that it is to handle, which in this case is accessed using the "palette" slot accessor, as described above.

The `pitch-seqs` generated by this method are created from combinations of a list of default `pitch-seqs` if not otherwise specified. The user can, however, modify these lists to shape the resulting pitch curves to his or her own taste. See the documentation for create-psps for more detail.

`  (create-psps (palette rch))`

Implementing the resulting palette and map

The resulting palette and map can then be passed directly to the `:rthm-seq-palette` and `:rthm-seq-map` arguments of the `make-slippery-chicken` function, as seen here:

```           :rthm-seq-palette (palette rch)
:rthm-seq-map rch)))```

The first system of the piece created by the above code will look like this in LilyPond output:

close

+ Using fibonacci-transitions instead of procession

The user also has the option of using the `fibonacci-transitions` algorithm instead of the `procession` algorithm to generate the resulting sequences. In order for the `make-rthm-chain` function to know to apply the `fibonacci-transitions` algorithm instead, the user must set one or both of the keyword arguments `:1-beat-fibonacci` (the 1-beat fragments) and `:slow-fibonacci` (the 2- and 3-beat fragments) to `T`.

```         :1-beat-fibonacci t
:slow-fibonacci t```

The rest remains the same

All other aspects of usage in conjunction with the `fibonacci-transition` function remain the same, e.g.:

```(let* ((rch
(make-rthm-chain
'test-rch 143
'((((e) e) ; 4 in total
(- s (s) (s) s -)
({ 3 (te) - te te - })
((e.) s)))
'((((q q) ; the 2/4 bars: 4 total
((q) q)
((q) q)
((q) (s) e.)))
((((e.) s (e) e (s) e.) ; the 3/4 bars: 4 total
(- e e - (e) e (q))
(- e. s - - +e e - (q))
(q (e.) s (q)))))
:players '(fl cl)
:1-beat-fibonacci t
:slow-fibonacci t)))
(create-psps (palette rch))
(let* ((mini
(make-slippery-chicken
'+mini+
:ensemble '(((fl (flute :midi-channel 1))
(cl (b-flat-clarinet :midi-channel 2))))
:set-palette '((1 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6))))
:set-map `((1 ,(ml 1 (num-rthm-seqs rch))))
:tempo-map '((1 (q 120)))
:rthm-seq-palette (palette rch)
:rthm-seq-map rch)))
(midi-play mini)
(cmn-display mini)
(write-lp-data-for-all mini)))```

The following is the LilyPond output of the same first system as the first example, this time using `fibonacci-transitions` for both the 1-beat and the slow voices:

close

+ An extra level of fibonacci-transitions

The `make-rthm-chain` function has an additional feature that allows the user to specify a further level of fibonacci-transitioning in the musical data that it generates. By specifying one or more additional sublists of fragments, the function will automatically perform a transition from the elements of the initial sets to the elements of each consecutive set.

This approach requires that each subsequent sublist have the same number of elements. It also requires that both the 2-beat and 3-beat fragments defined have the same number of additional sublists (the number of 1-beat sublists can differ from that of the 2- and 3-beat sublists). This approach can be used with both the `procession` and the `fibonacci-transitions` algorithms as the initial sequence generators.

```(let* ((rch
(make-rthm-chain
'test-rch 143
'((((e) e) ; 4 in total
(- s (s) (s) s -)
({ 3 (te) - te te - })
((e.) s))
(({ 3 (te) te (te) }) ; what we transition to
({ 3 - te (te) te - })
({ 3 (te) - te te - })
({ 3 (te) (te) te }))
(((q)) ; the second transition
(- s e s -)
({ 3 te (tq) })
(s (e.)))
((- e e -) ; the third transition
(- s s s - (s))
((32) 32 (e.))
((q))))
'((((q q) ; the 2/4 bars: 4 total
((q) q)
((q) q)
((q) (s) e.))
(({ 3 te+te te+te te+te }) ; what we transition to
(q - s e. -)
(q (s) e.)
(q (s) - s e -))
((q - e e -) ; the second transition
((e) q.)
(q - s (e) s -)
((s) q e.)))
((((e.) s (e) e (s) e.) ; the 3/4 bars: 4 total
(- e e - (e) e (q))
(- e. s - - +e e - (q))
(q (e.) s (q)))
(({ 3 (te) (te) te+te te+te } (q)) ; what we transition to
(- e. s - (q) (s) - s e -)
({ 3 te+te te } (q) q)
({ 3 - te te te - } (e) e { 3 (te) (te) te }))
((h.) ; the second transition
(h (q))
(- e e - +q (q))
(s (e.) (h)))))
:players '(fl cl))))
(create-psps (palette rch))
(let* ((mini
(make-slippery-chicken
'+mini+
:ensemble '(((fl (flute :midi-channel 1))
(cl (b-flat-clarinet :midi-channel 2))))
:set-palette '((1 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6))))
:set-map `((1 ,(ml 1 (num-rthm-seqs rch))))
:tempo-map '((1 (q 120)))
:rthm-seq-palette (palette rch)
:rthm-seq-map rch)))
(midi-play mini)
(cmn-display mini)
(write-lp-data-for-all mini)))```

The following two snippets show the difference in bars 23 to 38 between the first procession example, and the code above.

close

+ Some useful post-generation editing methods

Because of the fragmented nature of the source material for this featured algorithm, the resulting music may often be the point of departure for slightly more extensive use of post-generation editing methods. For example, the user may want to consolidate tied notes or multiple consecutive rests, add ties between existing notes or extend short notes over subsequent rests, or consolidate the very short bars into longer ones for easier reading. A number of methods are available to assist the user to these ends.

close

+ consolidate-notes

The `consolidate-notes` method combines any tied notes of a specified bar into one longer duration. This is a complex algorithm and is not fool-proof; for example, it does not split new durations evenly at the beats of the bar. But it can still be a very valuable aid in assembling a more legible score.

This method can be particularly useful with `rthm-chain` objects that were created from 2- and 3-beat fragments containing ties between beats, as can be seen in the following before-and-after snippets of the same passage.

The `consolidate-notes` method takes a single `rthm-seq-bar` object as its only argument. When applied to an existing `slippery-chicken` object, the bar object is best specified using the `get-bar` method, which takes the `slippery-chicken` object, the bar number, and the player ID as its own arguments, as can be seen here:

`(consolidate-notes (get-bar mini 72 'cl))`

This method is often automatically called from within other methods. In such cases, it can usually be disabled by setting the `:consolidate-notes` keyword argument to `NIL`, if the user so desires.

close

+ consolidate-rests-max

This method performs a similar consolidation routine to that of `consolidate-notes`, but applied to rests instead. As with the note-consolidating algorithm, this algorithm too is quite complex and not fool-proof, with the similar caveat that the resulting, longer rests are not split evenly at beats.

The result of calling this method on bar 2 of the flute part in the second `rthm-chain` object created above can be seen in these before-and-after snippets of the score:

As with the `consolidate-notes` method, this method takes a single `rthm-seq-bar` object as its only argument. This too can best be retrieved using the same `get-bar` syntax spelled out above:

`(consolidate-rests-max (get-bar mini 2 'fl))`

This method too is built-in to many other methods, and can generally be disabled in those methods by specifying the `:consolidate-rests` keyword with the value `NIL`.

close

+ tie-over-notes

One of the `rthm-chain` class's primary features, the automatic insertion of rests of various durations, may result in music that is initially a little pointillistic. One option for smoothing out passages of jagged short notes is the use of the `tie-over-rests` and related methods.

The `tie-over-rests` method changes all consecutive rests immediately following a specified note into notes of the same pitch as the note specified and ties them all together. It takes as its arguments the `slippery-chicken` object to be modified, the number of the bar in which the tie is to begin, the number of the note from which the tie is to begin, and the player ID.

`(tie-over-rests mini 72 4 'fl)`

This brief before-and-after example demonstrates the effect of using `tie-over-rests` on some of the notes from the original object.

By default, the `tie-over-rests` method also applies the `consolidate-notes` algorithm automatically to the bars affected. This behavior can be disabled by setting the method's `:consolidate-notes` keyword argument to `NIL`, e.g.:

`(tie-over-rests mini 72 4 'fl :consolidate-notes nil)`

It is important to note that this method changes the number of notes (non-rest `event` objects) in the specified bar and potentially any subsequent bars that begin with rests. Because of this, it may be necessary, when applying the method several times in the same player of a piece, to apply it in backwards order and set the `:consolidate-notes` argument to `NIL`.

It is also important to note that this method only affects the printable output. In order for any new ties to also be reflected in the MIDI output, the user must subsequently call the `handle-ties` method for the entire `slippery-chicken` object.

`(handle-ties mini)`

close

+ map-over-bars

Many of the post-generation editing methods only apply to one bar at a time. If the user would like to apply such a method to a longer passage of consecutive measures, the method `map-over-bars` can be very helpful.

This method takes as its arguments the `slippery-chicken` object to be affected, the start bar, end bar, and player ID (or list of IDs) for the part(s) to be affected, the method (or function) that is to be applied, and any additional optional arguments which that method or function may possess.

`(map-over-bars mini 72 77 'cl #'consolidate-notes)`

The result of this call to `map-over-bars` with the same passage used in the `consolidate-notes` example above can be seen in the score snippets here:

close

+ re-bar

The `re-bar` method is handy for redistributing musical material consisting of several short bars into fewer, longer bars. It is discussed in more detail in the tutorial for Second Law and Intraphrasal-Looping, but will be reviewed briefly here as well.

The `re-bar` method will only combine shorter bars into longer ones; it will not divide longer bars into shorter bars in order to conform to a strict time signature. In its simplest form, it takes as its arguments the `slippery-chicken` object to be re-barred, and a time signature passed to the `min-time-sig` keyword argument, which serves as the target time signature of for the re-barring process.

`(re-bar mini :min-time-sig '(4 4))`

It is important to note that because the method functions by merely combining consecutive bars, it will not always be possible to produce bars of the exact time signature given here (depending on the time signatures of the original bars), and the user should consider the value passed to this argument to be a generally descriptive target rather than a precise value.

The following two score snippets provide a before-and-after view of applying the `re-bar` method to the first `rthm-chain` object above.

close

The final `rthm-chain` method to be discussed here is `add-voice`. This method allows the user to automatically generate a new voice for the same original two-voice `rthm-seq-palette` and `rthm-seq-map` using the same fragment material specified in the initial call to `make-rthm-chain`.

When calling `add-voice`, the user must specify the `rthm-chain` object that is to be extended, the ID of the existing player from whose part the new voice is to be derived (coupled with the section ID of the `rthm-chain` object's `rthm-seq-map`, which defaults to `1`), and the player ID for the new part, e.g.:

`(add-voice rch '(1 fl) 'ob)`

The `add-voice` method must be called before `create-psps` is called, if the user intends to automatically generate `pitch-seq-palettes` for all parts, e.g.:

```  (add-voice rch '(1 fl) 'ob)
(create-psps (palette rch))```

If the `rthm-chain` object generated is to be used immediately in a call to `make-slippery-chicken` (as will usually be the case), the `:ensemble` defined for that function must also include a player with the same ID specified in the call to `add-voice`, e.g.:

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

The method generates its new part from the original part by first analyzing the time signatures of the `rthm-seq` objects of that source part. It collects the sequences of that first part into groups of same meter in the order they occur, and creates a new voice from different `rthm-seqs` of identical meter to the source voice.

The following is a small amount of source code and an accompanying score fragment of the music generated by that code produced by LilyPond:

```(let* ((rch
(make-rthm-chain
'test-rch 173
'((((e) e) ; 4 in total
(- s (s) (s) s -)
({ 3 (te) - te te - })
((e.) s)))
'((((q q) ; the 2/4 bars: 4 total
((q) q)
((q) q)
((q) (s) e.)))
((((e.) s (e) e (s) e.) ; the 3/4 bars: 4 total
(- e e - (e) e (q))
(- e. s - - +e e - (q))
(q (e.) s (q)))))
:players '(fl cl))))
(create-psps (palette rch))
(let* ((mini
(make-slippery-chicken
'+mini+
:ensemble '(((fl (flute :midi-channel 1))
(ob (oboe :midi-channel 2))
(cl (b-flat-clarinet :midi-channel 3))))
:set-limits-high '((fl (0 e6 100 e6))
(ob (0 b5 100 b5))
(cl (0 d5 100 d5)))
:set-limits-low '((fl (0 gs5 100 gs5))
(ob (0 a4 100 a4))
(cl (0 e3 100 e3)))
:tempo-map '((1 (q 72)))
:set-palette '((1 ((cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6))))
:set-map `((1 ,(ml 1 (num-rthm-seqs rch))))
:rthm-seq-palette (palette rch)
:rthm-seq-map rch)))
(midi-play mini)
(cmn-display mini)
(write-lp-data-for-all mini)))```

close