In the last post, I coded Steve Reich's Clapping Music. Seeing how a compositional procedure can be translated into code is interesting - even inspiring - but the code itself has limited use value for a composer because no matter how many times you run it, you will always get the same thing. There's no way to change the results without changing the code. There's no place in the code itself to adjust the outcome. This lack of flexibility makes the code less useful as a compositional tool. To have this flexibility requires variables. In this post, I'll rewrite the code to allow for this, creating not Steve Reich's Clapping Music, but instead Peter Edwards' Clapping Machine, which you are welcome to steal and rename <YourName>'s Clapping Machine. To achieve this, I need to make a function based on the Clapping Music program.
Below is all of the code needed for Clapping Music. There are two variables called orig-rhythm and rotated-rhythm as well as a def-score macro that generates the score. This was covered in the previous post.
(setf orig-rhythm
'(1/8 1/8 1/8 -1/8 1/8 1/8 -1/8 1/8 -1/8 1/8 1/8 -1/8))
(setf rotated-rhythm
(loop for r from 0 downto -13 collecting
(gen-rotate r orig-rhythm)))
(def-score clapping-music
(:title "Clapping Music"
:key-signature 'atonal
:time-signature '(12 8)
:tempo 168)
(rotating-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (loop for r-list in rotated-rhythm
collecting (gen-repeat 8 r-list))))
(fixed-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (gen-repeat 104 orig-rhythm))))
This can be slightly modified to create a single function that generates the piece. The defun macro is used to make functions. The first line of the defun macro takes a name and a set of arguments placed in parentheses. Below the defun macro is named clapping-machine and a pair of empty parentheses are given. This function will have no arguments, so there is nothing to place inside the brackets. Nonetheless, the brackets must be there.
(defun clapping-machine ()
Next, the variables need to be stated. The defun macro uses the operator let for this.
(let* ((orig-rhythm
'(1/8 1/8 1/8 -1/8 1/8 1/8 -1/8 1/8 -1/8 1/8 1/8 -1/8))
(rotated-rhythm
(loop for r from 0 downto -12 collecting
(gen-rotate r orig-rhythm)))
The let operator takes a list of lists. Each list has 2 values, the first is the variable name and the second is the data assigned to that name. Note that the let operator has an asterisk next to it (let*). This means that the generation of the variables is executed sequentially, allowing later variables to reference earlier ones. That's needed here because rotated-rhythm uses the orig-rhythm data in its loop.
Following this, the def-score macro is added. So, the whole function looks as follows:
(defun clapping-machine ()
(let* ((orig-rhythm
'(1/8 1/8 1/8 -1/8 1/8 1/8 -1/8 1/8 -1/8 1/8 1/8 -1/8))
(rotated-rhythm
(loop for r from 0 downto -12 collecting
(gen-rotate r orig-rhythm)))
(def-score clapping-music
(:title "Clapping Music"
:key-signature 'atonal
:time-signature '(12 8)
:tempo 168)
(rotating-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (loop for r-list in rotated-rhythm
collecting (gen-repeat 8 r-list))))
(fixed-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (gen-repeat 104 orig-rhythm))))
With this, I only need to call the function clapping-machine to generate the score for Clapping Music.
(clapping-machine)
The function can be modified so that it generates multiple versions of Clapping Music using the same underlying design. One way to do this is to add an argument that would serve as the original rhythm.
(defun clapping-machine (base-rhy)
(let* ((orig-rhythm base-rhy)
(rotated-rhythm (loop for r from 0 downto -12 collecting (gen-rotate r orig-rhythm))))
(def-score clapping-music
(:title "Clapping Music"
:key-signature 'atonal
:time-signature '(12 8)
:tempo 168)
(rotating-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (loop for r-list in rotated-rhythm collecting (gen-repeat 8 r-list))))
(fixed-clapper
:omn (make-omn
:pitch (gen-repeat 832 'f4)
:length (gen-repeat 104 orig-rhythm))))))
In the code above, the argument - that's in the first line between the parentheses - base-rhy has been added (and highlighted in red). Note that the list of rational numbers given with the variable orig-rhythm - '(1/8, -1/8, ...) - has been replaced with this argument. If I now run this function as follows, then I will get the Steve Reich's Clapping Music.
(clapping-machine '(1/8 1/8 1/8 -1/8 1/8 1/8 -1/8 1/8 -1/8 1/8 1/8 -1/8))
The list of rhythmic values above is the same as orig-rhythm. In fact, I could just write the following:
(clapping-machine orig-rhythm)
But I could also put in a different series of rational values to get different versions of Clapping Music. Below is one possibility.
(clapping-machine '(1/8 -1/8 1/8 1/8 -1/8 1/8 1/8 1/8 -1/8 -1/8 1/8 1/8))
For easier visualization, this rhythm is given below. It's clearly different from Reich's original, but shares some important and, as we'll see soon, necessary similarities.
The added flexibility is welcome, but we are, in fact, still very limited in what the argument can include. For starters, it must be 12 values long. Why? Because the rotated-rhythm variable will iterate from 0 to -12 regardless of how long base-rhy is. If base-rhy were 18 values, then the resulting score would only include 2/3 of the work; and if it were 6 values, then the resulting score would include the piece repeated twice. Furthermore, the base-rhy in this function must include exactly 8 positive and 4 negative values because there will be exactly 832 pitches created in the score. So, 8 attacks repeated 8 times for 13 iterations is required by the code.
Hence, additional adjustments to the code are necessary to lend it flexibility. If one wanted to be able to allow base-rhy to include any combination of positive and negative 1/8 values, for instance, then within the function the length of base-rhy could be multiplied by its number of positive values as well as the value of 8, determining the number of times the pitch F4 needs to be repeated. If added within the let operator with a variable name of num-pitches, it would look like the following:
(num-pitches (* (length base-rhy)
(length (remove-item '-1/8 base-rhy))
8))
Generate a variable called num-pitches and set to it the result of multiplying the length of base-rhy, the length of only the positive values in base-rhy, created by finding the length of the resulting list generated by removing the negative values of -1/8 from base-rhy, and the integer 8.
Then, within the make-omn functions, which are inside of the def-score macro, the code to generate the pitches could be changed from:
(gen-repeat 832 'f4)
into:
(gen-repeat num-pitches 'f4)
This would allow the function to adjust the number of pitches based on the content of base-rhy.
The point here is that some decisions need to be reached concerning both the means of adding flexibility and the kinds of desired flexibility. Composers working in this way would take some time to figure out how they will use this function in their composition and then modify it accordingly.
I want to let the computer use its pseudo-brain to churn these suckers out. So, I'm going to rework the code to allow the computer to generate versions of the base-rhy randomly. The rhythms in my Clapping Machine will all be 12 values long and include 8 positive and 4 negative values. Additionally, I want the first value to always be positive. This is fairly straightforward to do.
(append '(1/8)
(rnd-order
'(1/8 1/8 1/8 1/8 1/8 1/8 1/8 -1/8 -1/8 -1/8 -1/8)))
Append the value(s) in the first list of '(1/8) with the values in the second list that results from randomly ordering '(1/8 1/8 1/8 1/8 1/8 1/8 1/8 -1/8 -1/8 -1/8 -1/8).
A list of '(1/8), that's the first positive value, just needs to be combined with a random permutation of the list '(1/8 1/8 1/8 1/8 1/8 1/8 1/8 -1/8 -1/8 -1/8 -1/8), that is, a list with 7 positive values (since we already used 1 positive value for the downbeat) and 4 negative values. The append function is handy for this kind of task.
In the code below, the Clapping Music rhythm that was assigned to the orig-rhythm variable has been replaced with code that generates random orders of rhythmic values according to the guidelines outlined above. This is highlighted in red. Additionally, I want to be able to change the pitches. So, I will add two arguments called pitch-1 and pitch-2, highlighted in orange, and reference them in the body of the code, replacing the 'f4 value that was used previously. The pitch-1 argument will be assigned to the fixed rhythm; pitch-2 to the rotating one.
(defun clapping-machine (pitch-1 pitch-2)
(let* ((orig-rhythm
(append '(1/8)
(rnd-order
'(1/8 1/8 1/8 1/8 1/8 1/8 1/8 -1/8 -1/8 -1/8 -1/8))))
(rotated-rhythm
(loop for r from 0 downto -12 collecting
(gen-rotate r orig-rhythm))))
(def-score clapping-music
(:title "Clapping Music"
:key-signature 'atonal
:time-signature '(12 8)
:tempo 168)
(rotating-clapper
:omn (make-omn
:pitch (gen-repeat 832 pitch-2)
:length (loop for r-list in rotated-rhythm
collecting (gen-repeat 8 r-list))))
(fixed-clapper
:omn (make-omn
:pitch (gen-repeat 832 pitch-1)
:length (gen-repeat 104 orig-rhythm))))))
With this code, I will make a piece for 4 marimbas. Each marimba will have pitches from the overtone series for C2. Sometimes, a single hand will play 2 pitches.
(setf marimba-1 (clapping-machine 'c2 'c3g3))
(setf marimba-2 (clapping-machine 'c4e4 'g4d5)
(setf marimba-3 (clapping-machine 'bb4gb5 'g5b5)
(setf marimba-4 (clapping-machine 'd6fs6 'gs6cs7)
Combining these into a single score for 4 marimbas yields the work below. Listen closely for the rotations. It's subtle at times.
コメント