The easy way of serializing rhythm

15 Aug 2022

Constraints provide an important framework for creative tasks. One example is the use of serial techniques to create atonal music, allowing all twelve notes of the chromatic scale to be featured the same number of times in the composition. Eventually, twentieth-century composers began expanding the system to include any, and in some cases all, musical parameters, including duration, and there are a few ways of doing this.

One way is simply to slice a rhythm in N pieces and map each piece to an index, in which case the initial sequence, or “prime row” as it is called, is always 0–11 or 0–N depending of the length of the sequence, so to create a 3 by 3 matrix, for example, you simply shift each line one place to the right from the line above, and fill the empty spaces with the leftover (in parenthesis). For example,

0 1 2
  0 1 (2)
    0 (1 2)

becomes:

0 1 2
2 0 1
1 2 0

Note that no numbers repeat vertically or horizontally. The final step is to substitute all elements of the matrix with their corresponding rhythmic event, so if the original rhythm was xx. (012), its inversion (column 1) would be x.x (021) and so on and so forth.

Note also that this feature was added recently to the cl-patterns code base, making the process even easier to work with, so let’s play with it a little by serialising the opening bar of Steve Reich’s Clapping Music (1972), an 8-beat motif based on West African timelines.

'((0  . "x")
  (1  . "x")
  (2  . "x")
  (3  . ".")
  (4  . "x")
  (5  . "x")
  (6  . ".")
  (7  . "x")
  (8  . ".")
  (9  . "x")
  (10 . "x")
  (11 . "."))

Now we can use the index of the rhythm to generate the matrix.

(ql:quickload :cl-patterns)
(in-package #:cl-patterns)
(defparameter tones (mapcar #'car row))
(tone-matrix tones)
0 1 2 3 4 5 6 7 8 9 10 11
11 0 1 2 3 4 5 6 7 8 9 10
10 11 0 1 2 3 4 5 6 7 8 9
9 10 11 0 1 2 3 4 5 6 7 8
8 9 10 11 0 1 2 3 4 5 6 7
7 8 9 10 11 0 1 2 3 4 5 6
6 7 8 9 10 11 0 1 2 3 4 5
5 6 7 8 9 10 11 0 1 2 3 4
4 5 6 7 8 9 10 11 0 1 2 3
3 4 5 6 7 8 9 10 11 0 1 2
2 3 4 5 6 7 8 9 10 11 0 1
1 2 3 4 5 6 7 8 9 10 11 0

Each row contains all twelve pitch classes without repetitions, but for the purpose of generating rhythms, the numbers alone are not very useful, so we have to translate them into actual “notes”.

(let (notes)
  (loop for row in matrix
        do (push (mapcar (lambda (cell)
                           (cdr (assoc cell map)))
                         row)
                 notes))
  (reverse notes))
x x x . x x . x . x x .
. x x x . x x . x . x x
x . x x x . x x . x . x
x x . x x x . x x . x .
. x x . x x x . x x . x
x . x x . x x x . x x .
. x . x x . x x x . x x
x . x . x x . x x x . x
x x . x . x x . x x x .
. x x . x . x x . x x x
x . x x . x . x x . x x
x x . x x . x . x x . x

And to push our experiment one step further, let’s create an audible representation of the rhythmic transpositions by piping them into a real-time audio server, namely scsynth, so here’s what they sound like using Common Lisp to communicate with the server.

(ql:quickload :cl-collider)
(in-package #:cl-collider)

(setf *s* (make-external-server "localhost" :port 4444))
(server-boot *s*)
(jack-connect)

(defsynth woodblock ((gain 1))
  (let* ((env (line.kr 4 0 .01 :act :free))
         (sig (sin-osc.ar 700 0 env)))
    (out.ar 0 (pan2.ar sig 0 gain))))

(ql:quickload :cl-patterns/supercollider)
(cl-patterns:backend-start :supercollider)
(in-package #:cl-patterns)
(start-clock-loop :tempo 100/60)
(pb :beat
  :embed (pcycles notes :dur (* 3 12))
  :instrument :woodblock)

(play (list :beat))

The beat will loop indefinitely, similar to a drum machine, until you tell it to stop by running (stop t). Anyway, this was a brief introduction to serialising rhythm both manually and programmatically using cl-patterns and the Lisp programming language.

Reply by email
Other posts