23 – Thinking in Script: Mechanisms and Logic

The challenge of scripting languages : Generating Material with a Logical Test :  A ‘Cage Machine’ : The Limits of If…Then…Else : Implementing a More Complex System : Scripting a Hexadic Progression : Modular Composition with Progressions : Compound Operations

Throughout this book, we have often presented code by way of small, illustrative script snippets alongside occasional longer pieces, such as the 12-tone invention in chapter 10. Longer pieces, such as the aforementioned invention, often demonstrate a fairly linear flow of list processing as a way of demonstrating the technique and thinking behind scripted composition. However, there will be many compositional situations in which the making of the final piece will not necessarily follow such a linear ‘waterfall’ coding approach, and may need to make use of loops, logic and the preparation of bespoke functions (we encountered some of these in chapters 7, 12, 16 and 21).

This chapter explores the use of logical mechanisms and mathematical operators. These two methods of interpreting and manipulating data are fundamental to the practice of scripting and programming at large. At its most basic, a logical operation is a test or series of tests, for example:

bk1

This statement would look at a variable called ‘a’ and ask itself a number of questions. If a is either equal to one 1, or more than 10 then a course of action is taken – e.g. setting the variable b to 2 or 4 respectively. Otherwise, if a is any other number then b is set to 0. Naturally, we can use such statements to respond to changes in variables within our scripts. A mathematical operator is usually a simple mathematical process, such as add, subtract, divide or multiply.

Within many of the commonly invoked functions that we have encountered in this book are often series of such logical and mathematical operations. These allow functions to react appropriately to their various parameters, and also helps them to perform complex list manipulations in order to yield an output. Often we find ourselves in compositional situations where we too need to develop our own functions, or make use of such logical and mathematical operations.

One instance in which we may particularly need to do this is in creating script-based realisations of other compositional systems. In this chapter, we will look how a knowledge of logical and mathematical operations is necessary particularly to implement formalised yet inherently speculative systems such as those explored by John Cage.

Generating Material with a Logical Test

It is well known that John Cage used the Chinese oracle of the I-Ching as his ‘random number generator’. The I-Ching consists of sixty-four hexagrams, or figures each consisting of six broken and unbroken lines. Such hexagrams are usually generated by a process of flipping coins or picking handfuls of yarrow stalks in order to decide whether each line is broken or unbroken.

bk2

What is less well known is that Cage used computing to generate chance material throughout his career, an interest stimulated by his relationship with Lejaren Hiller who wrote FORTRAN code to generate large amounts of I-Ching hexagrams, dice rolls and so on. Throughout the 1980s, Cage would also work with composer-programmer Andrew Culver to develop bespoke code for his compositional needs.

Of course, such programmes only developed what might be considered initial, pre-compositional materials, for example, chairbar was the name of a programme which generated chair positions for Cage’s Essay installation in Barcelona. However, with the notion of a composing continuum in mind, in which the composer can smoothly go from script to notation, we might begin this chapter by revisiting one of Cage’s enduring works, the Music of Changes.

Music of Changes was essentially composed using lists of I-Ching hexagrams and a series of charts. Each of these – for parameters of ‘sound’ (pitch), duration and dynamic – contained sixty-four cells: one for each hexagram. Each unit of the score could, therefore, be generated from three hexagrams. Below are some excerpts from the charts, taken from John Pritchett’s excellent study of Cage, Throwing Sound into Silence (1993):

bk3

Note that the duration charts consisted of thirty-two sounding durations, and thirty-two rest durations. By generating three hexagrams, Cage could pick a sound, duration and dynamics event from each table and combine them to create music which was complex, unpredictable, yet subtly ‘self-similar’. Here is an example of how cells from sound and duration charts might come together, once again from Pritchett’s book:

bk4

It can be discerned from Cage’s approach that his charts are essentially the same as the core parameters that this book has concerned itself with: pitch, duration and dynamic. To prepare ourselves for implementing a more complex compositional system later in this chapter, we will consider how we may automatically generate such Cage-like parametric tables using function definitions and logical operations.

Consider that we may, for example, wish to generate a chart of pitch materials to use in our piece. To begin with, we might generate pitches that are either chords or melodies, which are highly chromatic, and which possibly span several octaves. We could create a function to do all this as follows:

bk5

This code may look complex and also introduces our first logical statement, but if we work through it methodically, the process should become clear:

  • (defun gen_notes (num_notes)
    Here is the function definition. The function also takes a parameter (num_notes), which tells it how long the generated fragment will be. Calling (gen_notes 3), for example, will either return a three-note melody, or a three-note chord.
  • (setf noteseq (gen-eval num_notes ‘(rnd-pick (gen-integer 0 23))))
    This code creates a series of notes (equal to the value of num_notes). Gen-eval will run the code (rnd-pick (gen-integer 0 23)) a number of times, to create a series of integers:

bk6

  • The chosen numbers are then translated into pitches and further transposed as a group between -12 and 12 semitones:

bk7

  • The next decision is to decide whether to chordize the result, or retain it as a melody. First a ‘switch’ is created: if the value is 1, then we are going to chordize the fragment, otherwise we will retain it as a melody:

bk8

  • In order to use the above chordize_seq variable to decide whether to chords or not, we have to invoke our first logical function: the ‘if’ statement. The following code says that ‘IF chordize_seq IS EQUAL TO 1 THEN chordize the fragment, OTHERWISE output a melodic version’:

bk9

The code above is the last statement in the function – the function will ‘return’ whatever the its last output was (e.g. a chordized or melodised fragment), for example:

bk10

Looking at the earlier example of Cage’s ‘sound’ chart, it is evident that his fragments of pitch material mix melodic and chordal units. So to create more complex fragments of our own, we will need to run the gen_notes function multiple times. We could begin with a construction such as (1 3 2) which we might take to mean ‘call gen_notes three times, and create fragments of 1, 3 and 2 tones in size’. We could create these constructions using the following code – note the inclusion of a zero value and its subsequent filtering so that not all outputs will have three collections of tones:

bk11

Wrapping this in a ‘lambda function’ allows us to call gen_notes for each item in the list. We could further encapsulate the compound statement in its own function:

bk12

To generate a chart consisting of sixty-four fragments, we then need only call the above sixty-four times:

bk13

It is a straightforward task to create similar series of fragments for duration and dynamics – see the first appendix to this chapter for some example code. Such materials may then be brought together into a composition by randomly sampling from the resulting charts and creating OMN representations thereof:

bk14

The Limits of If…Else…Then

The earlier code outlined a simple use of a logical operator – the ‘if’ statement. You may remember that it is constructed like this:

bk15

This is fine for testing a simple ‘if-else’ condition, but what if there were more possible values to test? This could quickly get complicated:

bk16

Implementing a More Complex System

The next example, which is more involved, will look at how we may handle such lengthy strings of conditions effectively. The object of study is the ‘Hexadic’ method of composition, developed by underground guitarist Ben Chasny (Six Organs of Admittance, Comets on Fire, Rangda). Hexadic is a method of atonal composition originally developed for guitar, which uses the 36 notes in three octaves to create twelve potential tonalities in each composition. In 2015 Chasny’s record label, Drag City, published a book and two albums of Hexadic music, which also toured extensively in the USA and Europe.

The Hexadic system presents a mode of indeterminate composition, in which a deck of playing cards is used as the ‘random number generator’, similar to Cage’s use of the I-Ching hexagrams. One reason for choosing these was, as Chasny points out, the historical mystique attached to the cards, but also there are the possible parametric natures of the cards themselves: they may be associated with tones or frets on the guitar, the also have numbers and suits which may also be interpreted compositionally. At the heart of the system is the Hexadic figure, which is an arrangement of six groups of six cells in the following manner:

bk17

The 36 cells in the figure are populated from a deck of 36 playing cards, drawn from the suits of hearts, diamonds and clubs. Each card is associated with a particular semitone and octave on the guitar – hearts are the lowest octave (E3-D#4), diamonds the central octave (E4-D#5), and clubs the highest octave (E5-D#6). The sequence by which each suit of cards is aligned to semitones is as follows:

bk18

We could begin encoding this deck of possible cards/tones into a list, for example:

bk19

Here, the cards are encoded with the first two letters of the suit, followed by the value of the card: hek = king of hearts, cl1 = ace of clubs, and so on.

To begin a composition with the Hexadic system, the cards are shuffled and then laid laid out in the form of the Hexadic figure, beginning by putting a card in the first cell, and so on until all 36 cells are filled:

bk20

We can replicate such a process easily enough using a very simple function, such as rnd-order:

bk21

This tells which cards belong to the 36 cells of the figure, and we can easily find out what is in a particular cell by using a function such as nth:

bk22

At this point, we might also identify the necessity to have a method of extracting the various significances attached to each card, primarily the association of a card with a tone. Based on what we already know, we could use a series of nested ‘if’ statements:

bk23

As you can see, this would get very complicated if applied to the full deck – each card needs a new nested if/else condition. We would also need to use a lambda function to process every item in the list against the if statements, which would in turn have to be wrapped in their own function.

This introduces the need to create a lookup system, in which we can easily translate between card values and tones without complicated nested statements and invoking lambda functions at every turn. Fortunately, this can be easily implemented with the def-case function, which creates a series of associated pairs, linking card name to a pitch identifier:

bk24

We can then easily translate our card names into pitches, by invoking the newly defined ‘case’ called card-pitches as follows:

bk25

bk26

As can be seen, the first arm comprises the first six cells. The second comprises of the next six cells, and so on. We can easily create our list of possible arms by simply dividing our list of shuffled cards into sublists of six items:

bk27

We can then define the six-note groups for each arm by processing the above list:

bk28

The poles are a little more complex – each pole contains half of the material of each opposing arm. This creates a series of unique, yet similar, tonalities that lend a consistency to the tonal language of the system:

bk29

The above code uses filter-first and filter-last to take the first three values from one ‘arm’ and the latter three from the opposing one in order to create the pole. So, pole i consists of the fist half of arm A, and the second half of arm D – as can be seen in the earlier illustration of the Hexadic figure.

Scripting a Hexadic Progression

The next stage of composing with the system requires further use of a number of logical and mathematical operators. The process of deciding which tonal groups are used in the composition is worked out by plotting a ‘progression’ across the figure. A progression tells the player or composer which arm/pole to begin with, and also which tone within the arm/pole is considered the ‘interval tone’: in the Hexadic system, each arm/pole in the progression has a root tone (the lowest tone in the group) and variable interval tone to which is given particular importance akin to the dominant in a conventional scale. There may also be a ‘centre-tone’ throughout the piece as a whole, which is a pitch class that may occur in all octaves regardless of the particular arm/pole being used to compose.

We can potentially begin our composition with the choice of a centre-tone – the centre-tones are mapped to the spade cards, and denote one pitch class each:

bk30

Since these cards had already been associated with pitches in the (def-case card-pitches) statement, we can create a series of centre-tones using a simple octave transposition:

bk31

The procedure here is rather simple, although it makes use of a series of nested statements:

  • (card-pitches ‘(sp3))
    This code uses our earlier def-case statement to relate the card to a pitch (g6)
  • (gen-repeat 5…)
    Repeats the given pitch five times (g6 g6 g6 g6 g6)
  • (pitch-transpose-n ‘(0 -12 -24 -36 -48) …
    This code then transposes each item in the list by the given intervals, essentially spreading the pitch across five octants (g6 g5 g4 g3 g2)
  • (sort-asc …
    Throughout these compositions we will always sort pitches into ascending series for ease of use. Sorting the centre tones means we can easily ‘mix’ them into whichever arm/pole we are using as our current tonality.

To begin a progression across the figure, a cell is first chosen. If this cell contains a red card would mean that we use the corresponding ‘pole’, a black card would mean we use the ‘arm’ to which it corresponds. If, for example, we chose to start in the cell containing the card ♣3, we can easily discern that the ♣3 card is in cell number 28, and that it would correspond to pole iii (see the illustration below). The next progression is calculated by moving on the number of steps indicated by the card’s value (e.g. 3), which would take us to cell 31 (♣6 and pole vi):

bk32

We can evidently see what will happen next – counting six places on from cell 31 would take us back to the first cell the figure, which is 5/Arm A. However, in terms of the programming we have encountered thus far, creating such a mechanism is going to be difficult: to realise a flexible script for working with the Hexadic figure, generating progressions, and so on, we will need to turn our attention to mathematical and logical operators. Note that this is something of a simplification for the purposes of this chapter: in the Hexadic system proper, the card value opposite the current cell is usually taken as the number of cells to progress by (this would be ♣8, or 8 steps, in the above diagram).

Given that we have decided to begin our progression with the ♣3 card, we can easily work out which cell this corresponds to, by finding its position within the deck as it was earlier shuffled to form the Hexadic figure:

bk33

To explain this code further:

  • (flatten shuffled-deck)
    This code flattens the previously ‘shuffled’ set of tones that are associated with the cells of the Hexadic figure. Previously they had been divided into groups of six in order to derive the arms/poles: (a3 bb4 eb6 cs5 cs6 eb5 c6 e5 e4 c4 g3 eb4 cs4 g4 b5 fs3 fs4 d6 fs5 f3 gs3 b3 f5 a4 d5 c5 d4 g5 e3 b4 bb5 f4 gs4 gs5 a5 bb3)
  • (car (card-pitches interval-card))
    This invokes our card-pitches def-case statement to relate the given interval card to a tone: (cl3) becomes (g5). However, the position statement does not work with lists, only solitary values. For this reason the LISP primitive ‘car’ is used, which returns only the first (and sole) item int the list.
  • (position …
    Finally, this statement looks for the position of the tone within the shuffled deck. It returns 27, which corresponds to the 28th cell of the Hexadic figure (remember, in scripting we start counting with 0!)

We also need to know whether the card we have selected (♣3) is a black or red suit. We can do this bit of logic easily with a further def-case definition, using 1 for red suits (indicating an arm), or 0 for black suits (indicating a pole):

bk34

We can therefore work out if ♣3 corresponds to a 1 or 0 in this scheme:

bk35

So, we now know that the spade card corresponds to a pole, we have to work out exactly which one. Looking at the diagram above, you can see that the cell containing the ♣3 card corresponds to pole iii. If the same cell instead contained a card from a red suit, it would correspond to arm E.

We can use def-case once again to answer these questions. We can create two further ‘lookup tables’, one to work out which arm corresponds to the selected cell when the card is red (interval-cell-to-arm), and one to work out the corresponding pole when the card is black (interval-cell-to-pole).We can then use a further def-case (assign-hexafield) to work out which to choose:

bk36

Note that assign-hexafield will set a variable (current-hexafield-n) whenever it is called: a def-case statement need not be a simple act of substitution as we have seen earlier, but can contain more complex functions, in the spirit of an ‘IF’ statement in conventional programming languages. After executing the last line of the above script, current-hexafield-n will have the value ‘(8). Why 8? In order to have all the arms and poles easily available to the above process, we bring them together:

bk37

We can then, for example, find the current pole or arm in our progression by calling:

bk38

The code we have developed so far, heavily reliant on the logical capabilities of def-case, has allowed us to do the following:

  • Select a card from the Hexadic figure (the interval card)
  • Work out whether it corresponds to an arm or pole
  • Work out exactly which arm or pole the cell corresponds to
  • Assign these arm/pole tones to the a new list (current-hexafield)

Because we may want to work out our progressions in advance of composing, we might begin to create lists of the Hexadic fields (e.g. arms/poles) used in the piece, as well as the interval cards that have been selected:

bk39

The final piece of the puzzle, and the one that requires some engagement with LISP’s mathematical operators, relates to working out how to progress from our current interval card, to the next one. Obviously we need to work out how to translate a card value to a number of steps across the Hexadic figure, given that we are not only dealing with numbers, but also ‘face’ cards (jack, queen, king) which also need assigning values. The obvious way to do this, of course, is to have recourse to another def-case definition. Here, the face cards are assigned values 11, 12 and 13:

bk40

We can see from the above that cl3 will corresponds to the number 3, indicating that the next progression will be informed by the card 3 cells away from the present interval card. We can easily add 3 to the value of our current cell (27), to work toward creating a progression, for example:

bk41

What would happen, however, if the output of this function exceeded 35, which corresponds to the last cell in the Hexadic figure? We know that we would ‘wrap around’ to the first cell, although our code needs a little modification to take this into account, using the mod function. ‘Mod’ is short for ‘modulo’, an element of modular arithmetic in which numbers are made to ‘wrap around’ when they reach an upper limit. We can use this function to constrain the output to values between 0 and 35:

bk42

We have now created a basic system that allows us to plot subsequent progressions across the Hexadic figure, while storing the resulting Hexadic fields and Interval Tones. We could encapsulate this into a function, which we can call whenever we want to create an additional progression:

bk43

Now, whenever we wish to generate the next progression across the figure, all we need to do is call (next-progression). Having generated our first progression, we could call (gen-eval 5 ‘(next-progression)) to give us the following six Hexadic fields (poles or arms) and interval tones:

bk44

Modular Composition with Progressions

Note that the Hexadic fields are sorted in ascending order. The basic premise of the system is that each field has a root tone (the lowest) and an ‘interval tone’, both of which provide poles of tension for the music to play around. There are also the centre tones, which might also be interpolated into the available tones. For example, we could ‘mix’ these tones in with our existing progressions thus:

bk45

Gen-mix will essentially combine two series of lists or lists of lists together. Note that with single lists the output is discretely grouped:

bk46

And with lists of lists it is as follows:

bk47

Hence the need to invoke sort-asc in the above example to coherently integrate the centre-tones into the other tonal material in each progression.

We can begin composing with the progressions, for example an Arvo Part-inspired three part piece (left and right hands of the piano, plus low pedal drone between the each section):

bk48

The above code can also be encapsulated into a function. Once this is done, all that would be needed to generate further variations on the piece is to update the progress-marker variable and then call the function:

bk49

You will notice that the above code only generates pitches. We can return to mathematical operators to enable us to work out appropriate durations for each phrase. After six progressions, the pitches contained in the lower part (p1) look like this:

bk50

We know that for each progression, four notes are played in the lower part. We can create a rhythmic plan, based on 1/2 not durations, and also work out how many repetitions of this are required to effectively map to our list of pitches. Given that each selection involved four notes, we know that we can divide the length of the phrase by 4 to find out how many repetitions of the rhythmic material are required:

bk51

Of course, we could have used (gen repeat 6 …), but using an operator allows us to build further flexibility into our script. Below is the draft notation output from Opusmodus – the first note in the right hand of bar three would need removing or bracketing in MusicXML, since in this case the root and interval tone are the same.

bk52

You can hear further compositions, using more complex variations on the progression mechanics, on Phil Legard’ Sorath album, and read more about the composition of the album on these two blog posts.

Compound Operations

We have seen how def-case can serve in place of complex logical mechanisms, and even have more complex results embedded with in it using nested functions. We have also observed the use of the mathematical operators ‘-‘ and ‘\’ (subtract and divide). At this point, it should be mentioned that the operators can take any number of arguments, for example:

bk53

Naturally, a working knowledge of how to apply these operators is greatly beneficial to the practice of composing using numeric intervals. Another Hexadic piece, Thirteen Auras (for Matt Marble), was composed by applying intervallic thinking to the idea of Hexadic progressions.

Take, for example, the following Hexadic field:

bk54

What if cards were associated with intervals instead of tones? We could assign an intervalic value between 1 and 4 steps to every card instead:

bk55

We could the use these values to construct a series of scales ascending from a given interval. For example, given a tonal centre of e4, we could use the above intervals to develop this scale:

bk56

All of the logical and mathematical approaches we have encountered so far can be used to do this. Naturally, a def-case construct can be used to relate card values to intervals, and once this is done one only needs to call gen-accumulate, which is a function that will progressively add values together:

bk57

We can then easily transpose these upward from any given tonal centre to generate a scale, for example, from e4:

bk58

Conversely, a series of mirrored ‘undertones’ can also be generated:

bk59

Which, added together yields the following sets of intervals:

bk60

The use of complementary intervals developing around a central pitch is reminiscent of the construction of harmonic materials in Lindberg’s Twine, a piece that has been mentioned previously. In Thirteen Auras, central pitches, around which the intervals are developed, are derived from a random ordering of 13 chromatic pitches (e4-e5):

bk61

Furthermore, thirteen progressions around the Hexadic figure are generated – these include repeated collections of intervals, although they will, of course, be applied to different central pitches. We could visualise the arrangement of the composition as follows:

bk62

The section numbers shown in red are played as chords, and will help you to hear the consequence of the application of intervals to the underlying chromatic progression.

This piece was developed as 13 small sections of script. Some are relatively simple, working with chordize functions applied to the material. Most of the other sections use random samples from the resulting overtone/undertone material, which are then interleaved to create repetitive movement across the sample. These can be used to develop melodic lines, for example:

bk63

We can create a simple rhythm to accompany this, in which the interleaved note in each list (e.g. the last note) is sustained – you can hear this process in the opening bars of the piece and used throughout the piece in different ways (with reversals of the ‘melody’, for example):

bk64

The notion of working in discrete scripted sections across a piece is especially suited to a series of progressions over tonal or intervallic material, as exemplified in the summary of the Hexadic system used in this chapter. In particular, it concentrates the mind on the problem of how to articulate or develop a small collection of pitches in as elegant a manner as possible. Furthermore, it encourages a good sense of flow – working on one section at a time focuses on the compositional challenge of progression, in terms of considering what went before and what comes after. In this respect, a section-by-section approach allows a composer to make quite radical changes from section to section (such as the switch to blocked chords in Thirteen Auras), rather than getting lost in generating reams of pre-compositional material and having to edit and process such material at a higher hierarchical level in order to realise a piece. Both approaches do, of course, have their advantages, disadvantages and particular suitabilities.

Appendices
A Cage Machine [opmo]
Hexadic Progressions [opmo]