Academia.eduAcademia.edu

Thinking in Script

2017, Parametric Composition

Chapter from 'Parametric Composition' (Morgan & Legard, 2017). 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.

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: IF a = 1 THEN b = 2 ELSE IF a > 10 THEN b = 4 ELSE b = 0 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 scriptbased 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. The first sixteen hexagrams of the I-Ching. 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 composerprogrammer Andrew Culver to develop bespoke code for his compositional needs. Of course, such programmes only developed what might be considered initial, precompositional 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):  The first eight cells of a ‘sound’ (or pitch) chart.  The first ten cells of a duration chart.  The first four cells of a dynamics chart. 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:  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: (defun gen_notes (num_notes) (setf noteseq (gen-eval num_notes '(rnd-pick (gen-integer 0 23)))) (setf noteseq2 (pitch-transpose (rnd-pick (gen-integer -12 12)) (integer-to-pitch noteseq))) (setf chordize_seq (rnd-pick '(0 1))) (if (= chordize_seq 1) (chordize noteseq2) (melodize noteseq2) ) ) 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: ; For example where num_notes = 3: (setf noteseq (gen-eval num_notes '(rnd-pick (gen-integer 0 23)))) => (2 6 18) • The chosen numbers are then translated into pitches and further transposed as a group between -12 and 12 semitones: (setf noteseq2 (pitch-transpose (rnd-pick (gen-integer -12 12)) (integer-to-pitch noteseq))) => (b4 eb5 eb6) • 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: (setf chordize_seq => 1 (rnd-pick '(0 1))) • 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’: (if (= chordize_seq 1) (chordize noteseq2) (melodize noteseq2) ) 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: (gen_notes 2) => (gs5d5) (gen_notes 3) => (f5 a4 eb5) (gen_notes 3) => (bb4g4b5) 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: (filter-remove '0 (append (rnd-number 1 1 3) (rnd-number 2 0 3))) ; May yield: => (2 3 1) => (3 1 3) => (2 3) => (1 2) => (1 1 2) ; etc. 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: (defun pitch_fragment () (setf out (mapcar (function (lambda (x) (gen_notes x))) (filter-remove '0 (append (rnd-number 1 1 3) (rnd-number 2 0 3))))) ) To generate a chart consisting of sixty-four fragments, we then need only call the above sixty-four times: (setf pitch_table (gen-eval 64 '(pitch_fragment)))  The first eight pitch fragments generated by the above code. 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: (setf pitch_picks (rnd-sample 64 pitch_table)) (setf duration_picks (rnd-sample 64 duration_table)) (setf dyn_picks (rnd-sample 64 dyn_table)) (setf pno-rh (filter-repeat 1 (make-omn :length (mcflatten duration_picks) :pitch (mcflatten pitch_picks) :velocity (mcflatten dyn_picks) ) :flat t)) 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: (if (test condition = true) (then do this) (otherwise do this) ) 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: (if (test condition = true) (then do this) (otherwise, if (test condition 2 = true) (then do this) (otherwise, if (test condition 3 = true) (then do this) (otherwise, do this) ) ) ) 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:  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 (E3D#4), diamonds the central octave (E4-D#5), and clubs the highest octave (E5D#6). The sequence by which each suit of cards is aligned to semitones is as follows: E F F# G G# A A# B C C# D D# ♥K ♥A ♥2 ♥3 ♥4 ♥5 ♥6 ♥7 ♥8 ♥9 ♥10 ♥J ♥Q ♦A ♦2 ♦3 ♦4 ♦5 ♦6 ♦7 ♦8 ♦9 ♦10 ♦J ♦Q ♣A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣10 ♣J We could begin encoding this deck of possible cards/tones into a list, for example: (setf basic-deck '(hek he1 he2 he3 he4 he5 he6 he7 he8 he9 he10 hej heq di1 di2 di3 di4 di5 di6 di7 di8 di9 di10 dij diq cl1 cl2 cl3 cl4 cl5 cl6 cl7 cl8 cl9 cl10 clj)) 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: We can replicate such a process easily enough using a very simple function, such as rnd-order: (setf shuffled-deck-c (rnd-order basic-deck)) => (he5 di6 clj di9 cl9 dij cl8 diq heq he8 he3 hej he9 di3 cl7 he2 di2 cl10 cl2 he1 he4 he7 cl1 di5 di10 di8 he10 cl3 hek di7 cl6 di1 di4 cl4 cl5 he6) 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: (nth 4 shuffled-deck-c) => cl9 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: (setf card 'he1) (if (STRING= card 'hek) (setf out '(e2)) (if (STRING= card 'he1) (setf out '(f2)) (if (STRING= card 'he2) (setf out '(fs2)) (if (STRING= card 'he3) (setf out '(g2)) ) ) ) ) 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: (def-case card-pitches ; Hearts (he) (hek 'e3) (he1 'f3) (he2 'fs3) (he3 'g3) (he4 'gs3) (he5 'a3) (he6 'bb3) (he7 'b3) (he8 'c4) (he9 'cs4) (he10 'd4) (hej 'eb4) (heq 'e4) ; Diamonds (di) (dik 'e4) (di1 'f4) (di2 'fs4) (di3 'g4) (di4 'gs4) (di5 'a4) (di6 'bb4) (di7 'b4) (di8 'c5) (di9 'cs5) (di10 'd5) (dij 'eb5) (diq 'e5) ; Clubs (cl) (clk 'e5) (cl1 'f5) (cl2 'fs5) (cl3 'g5) (cl4 'gs5) (cl5 'a5) (cl6 'bb5) (cl7 'b5) (cl8 'c6) (cl9 'cs6) (cl10 'd6) (clj 'eb6) (clq ‘e6) ; Spades (sp) (spk 'e6) (sp1 'f6) (sp2 'fs6) (sp3 'g6) (sp4 'gs6) (sp5 'a6) (sp6 'bb6) (sp7 'b6) (sp8 'c7) (sp9 'cs7) (sp10 'd7) (spj 'eb7) (spq 'e7) ) We can then easily translate our card names into pitches, by invoking the newly defined ‘case’ called card-pitches as follows: ; A simple example (card-pitches '(clk)) => '(e5) ; A more complex example (setf shuffled-deck-d (card-pitches shuffled-deck-c)) => (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) Of course, there is more to the system than simply shuffling the deck. The Hexadic figure actually defines twelve tonal collections containing six pitches each. These are known as ‘arms’ (letters A - F) and ‘poles’ (numerals i - vi):  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: (setf arms (gen-divide 6 shuffled-deck-d)) => ((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)) We can then define the six-note groups for each arm by processing the above list: (setf arm-a (nth 0 arms)) (setf arm-b (nth 1 arms)) (setf arm-c (nth 2 arms)) (setf arm-d (nth 3 arms)) (setf arm-e (nth 4 arms)) (setf arm-f (nth 5 arms))  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: (setf (setf (setf (setf (setf (setf pole-i pole-ii pole-iii pole-iv pole-v pole-vi (append (append (append (append (append (append (filter-first 3 arm-a) (filter-last 3 arm-a) (filter-first 3 arm-b) (filter-last 3 arm-b) (filter-first 3 arm-c) (filter-last 3 arm-c) (filter-last 3 arm-d))) (filter-first 3 arm-d))) (filter-last 3 arm-e))) (filter-first 3 arm-e))) (filter-last 3 arm-f))) (filter-first 3 arm-f)))  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: E F F# G G# A A# B C C# D D# ♠K ♠A ♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠10 ♠J Since these cards had already been associated with pitches in the (def-case cardpitches) statement, we can create a series of centre-tones using a simple octave transposition: (setf centre-tones (sort-asc (pitch-transpose-n '(0 -12 -24 -36 -48) (gen-repeat 5 (card-pitches ‘(sp3)))))) => (g2 g3 g4 g5 g6) 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):  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: (setf interval-card ‘(cl3)) ;; Eg. 3 of Clubs (setf interval-cell (list (position (car (card-pitches interval-card)) (flatten shuffled-deck)))) => (27) 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): (def-case interval-arm-or-pole ; Hearts (he) (hek 1) (he1 1) (he2 1) (he3 1) (he4 1) (he5 1) (he6 1) (he7 1) (he8 1) (he9 1) (he10 1) (hej 1) (heq 1) ; Diamonds (di) (dik 1) (di1 1) (di2 1) (di3 1) (di4 1) (di5 1) (di6 1) (di7 1) (di8 1) (di9 1) (di10 1) (dij 1) (diq 1) ; Clubs (cl) (clk 0) (cl1 0) (cl2 0) (cl3 0) (cl4 0) (cl5 0) (cl6 0) (cl7 0) (cl8 0) (cl9 0) (cl10 0) (clj 0) (clq 0) ; Spades (sp) (spk 0) (sp1 0) (sp2 0) (sp3 0) (sp4 0) (sp5 0) (sp6 0) (sp7 0) (sp8 0) (sp9 0) (sp10 0) (spj 0) (spq 0) ) We can therefore work out if ♣3 corresponds to a 1 or 0 in this scheme: (setf arm-or-pole (interval-arm-or-pole interval-card)) => (0) 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 defcase (assign-hexafield) to work out which to choose: ; ; arms: (def-case (0 0) (6 1) (12 2) (18 3) (24 4) (30 5) ) a b c d e f 0 1 2 3 4 5 interval-cell-to-arm (1 0) (2 0) (3 0) (7 1) (8 1) (9 1) (13 2) (14 2) (15 2) (19 3) (20 3) (21 3) (25 4) (26 4) (27 4) (31 5) (32 5) (33 5) ; ; poles: (def-case (0 6) (6 8) (12 10) (18 7) (24 9) (30 11) ) i ii iii iv v vi 6 7 8 9 10 11 interval-cell-to-pole (1 6) (2 6) (3 7) (4 7) (7 8) (8 8) (9 9) (10 9) (13 10) (14 10) (15 11) (16 11) (19 7) (20 7) (21 6) (22 6) (25 9) (26 9) (27 8) (28 8) (31 11) (32 11) (33 10) (34 10) (4 0) (10 1) (16 2) (22 3) (28 4) (34 5) (5 0) (11 1) (17 2) (23 3) (29 4) (35 5) (5 7) (11 9) (17 11) (23 6) (29 8) (35 10) (def-case assign-hexafield (0 (setf current-hexafield-n (interval-cell-to-pole interval-cell))) (1 (setf current-hexafield-n (interval-cell-to-arm interval-cell))) ) (assign-hexafield arm-or-pole) 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: (setf hexafields (sort-asc (append (list (list (list (list (list (list ))) arm-a) (list arm-b) arm-c) (list arm-d) arm-e) (list arm-f) pole-i) (list pole-ii) pole-iii) (list pole-iv) pole-v) (list pole-vi) We can then, for example, find the current pole or arm in our progression by calling: (setf current-hexafield (nth (car current-hexafield-n) hexafields)) => (e3 e4 b4 e5 g5 c6) The code we have developed so far, heavily reliant on the logical capabilities of defcase, 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: (setf hexafield-progressions '()) (setf interval-card-progressions '()) (setf hexafield-progressions (append hexafield-progressions (list current-hexafield))) => ((e3 e4 b4 e5 g5 c6)) (setf interval-card-progressions (append interval-card-progressions (card-pitches interval-card))) => ((bb5)) 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: (def-case progression-values ; Hearts (he) (hek 13) (he1 1) (he2 2) (he3 3) (he4 4) (he5 5) (he6 6) (he7 7) (he8 8) (he9 9) (he10 10) (hej 11) (heq 12) ; Diamonds (di) (dik 13) (di1 1) (di2 2) (di3 3) (di4 4) (di5 5) (di6 6) (di7 7) (di8 8) (di9 9) (di10 10) (dij 11) (diq 12) ; Clubs (cl) (clk 13) (cl1 1) (cl2 2) (cl3 3) (cl4 4) (cl5 5) (cl6 6) (cl7 7) (cl8 8) (cl9 9) (cl10 10) (clj 11) (clq 12) ; Spades (sp) (spk 13) (sp1 1) (sp2 2) (sp3 3) (sp4 4) (sp5 5) (sp6 6) (sp7 7) (sp8 8) (sp9 9) (sp10 10) (spj 11) (spq 12) ) 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: (+ (car interval-cell) (car progression-interval)) ; Which is the same as (+ 27 3) => 30  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: (setf next-progression-cell (mod (+ (car interval-cell) (car progression-interval)) 36)) 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: (defun next-progression () (setf interval-card (list (nth next-progression-cell shuffled-deck-c))) (setf interval-cell (list (position (car (card-pitches interval-card)) (flatten shuffled-deck)))) (setf arm-or-pole (interval-arm-or-pole interval-card)) (assign-hexafield arm-or-pole) (setf current-hexafield (nth (car current-hexafield-n) hexafields)) (setf hexafield-progressions (append hexafield-progressions (list current-hexafield))) (setf interval-card-progressions (append interval-card-progressions (card-pitches interval-card))) (setf progression-interval (progression-values interval-card)) (setf next-progression-cell (mod (+ (car interval-cell) (car progression-interval)) 36)) ) 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: ; Hexadic Fields ((e3 e4 b4 e5 g5 (a3 bb4 cs5 eb5 (fs3 cs4 fs4 g4 (arm/pole c6) cs6 eb6) b5 d6) tonalities) (fs3 f4 fs4 gs4 bb5 d6) (a3 bb4 cs5 eb5 cs6 eb6) (f3 gs3 cs5 eb5 fs5 cs6)) ; Interval tone for each of the progressions (g5 bb5 a3 eb5 fs4 fs5)  Six progressions, interval tones highlighted with red. 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: (setf progressions-centres (sort-asc (gen-mix (gen-repeat 6 (list centre-tones)) hexafield-progressions))) => ((g2 e3 g3 e4 g4 b4 e5 g5 g5 c6 g6) (g2 fs3 g3 f4 fs4 g4 gs4 g5 bb5 d6 g6) (g2 g3 a3 g4 bb4 cs5 eb5 g5 cs6 eb6 g6) (g2 g3 a3 g4 bb4 cs5 eb5 g5 cs6 eb6 g6) (g2 fs3 g3 cs4 fs4 g4 g4 g5 b5 d6 g6) (g2 f3 g3 gs3 g4 cs5 eb5 fs5 g5 cs6 g6)) Gen-mix will essentially combine two series of lists or lists of lists together. Note that with single lists the output is discretely grouped: (gen-mix '(a b c d) '(1 2 3 4)) => ((a 1) (b 2) (c 3) (d 4)) And with lists of lists it is as follows: (gen-mix '((a b c d)) '((1 2 3 4))) => ((a b c d 1 2 3 4)) 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): ; Use a variable to define which Hexadic field we are working ; with (0 = the first field in the progression) (setf progress-marker 0) ; Get the interval tone (setf itone (nth progress-marker interval-card-progressions)) ; Get the first Hexadic field (without centres) (setf hexaf (sort-asc (nth progress-marker hexafield-progressions))) ; One part (the lower part) will play the root tone (e.g. lowest ; pitch) twice, followed by the second and third tones (setf p1 (append (list (nth 0 hexaf) (nth 0 hexaf) (nth 1 hexaf) (nth 2 hexaf)) ) ) ; The upper part ; the last three ; movement: (setf p2 (append itone (nth 3 (nth 4 (nth 5 (nth 4 (nth 3 ) )) will always play the interval tone, followed by pitches in the Hexadic field in an arc-like (list hexaf) hexaf) hexaf) hexaf) hexaf) ; The lowest part will always be the lowest centre tone (setf p3 (append (list (nth 0 centre-tones))) ) 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: (setf progress-marker 1) (nextsequence) 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: (e3 e3 e4 b4 fs3 fs3 f4 fs4 a3 a3 bb4 cs5 a3 a3 bb4 cs5 fs3 fs3 cs4 fs4 f3 f3 gs3 cs5)  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: (setf r1 (gen-repeat (/ (length p1) 4) '(-1/2 1/2 1/2 1/2 1/2 -1/2))) 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. You can also listen to the short piece here.  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: (+ 5 4 3 2 1) => 15 (/ 9 3 3) => 1 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: Cards: Tones: ♣10 ♥10 ♥1 d6 d4 f3 ♦8 c5 ♦9 cs5 ♣7 b5 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: Cards: Intervals: ♣10 ♥10 ♥1 1 3 2 ♦8 2 ♦9 3 ♣7 2 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:  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: (setf overtones (gen-accumulate hexadic-intervals)) => (1 4 6 8 11 13) ; e.g. 1 = 1 ; 1 + 3 = 4 ; 1 + 3 + 2 = 6 ; 1 + 3 + 2 + 2 = 8 ; 1 + 3 + 2 + 2 + 3 = 11 ; 1 + 3 + 2 + 2 + 3 + 2 = 13 We can then easily transpose these upward from any given tonal centre to generate a scale, for example, from e4: (setf overtone-scale (pitch-transpose-n overtones (gen-repeat 6 'e4)) ) => (f4 gs4 bb4 c5 eb5 f5) Conversely, a series of mirrored ‘undertones’ can also be generated: (setf undertones (integer-invert overtones)) (setf undertone-scale (pitch-transpose-n undertones (gen-repeat 6 'e4)) ) => (eb4 c4 bb3 gs3 f3 eb3) Which, added together yields the following sets of intervals:  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): '(c5 eb5 gs4 cs5 d5 f4 fs4 as4 e5 g4 b4 a4 c5) 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: Section Centre Tone I II III IV V VI VII VIII IX X XI XII XIII c5 eb5 gs4 cs5 d5 f4 fs4 as4 e5 g4 b4 a4 c5 Intervals from Hexafields (applied as overtones and undertones) (1 3 2 2 3 2) (1 2 1 3 1 4) (3 2 1 4 1 1) (4 4 2 2 3 2) (4 3 2 1 2 1) (4 3 2 1 2 1) (4 4 2 2 3 2) (3 2 1 4 1 1) (4 3 2 1 2 1) (4 3 2 1 2 1) (4 4 2 2 3 2) (3 2 1 4 1 1) (4 3 2 1 2 1) 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: ; ; ; ; Create a nine-note melody by generating a series of numbers between 6 and 11. Position-filter can then use these numbers as indices to choose from a combined list of overtone and undertone scales: (setf samples (position-filter (rnd-sample 9 '(6 7 8 9 10 11)) over-under-tone-aggregate)) => (fs5 f6 cs6 e5 fs5 gs5 cs5 f6 f6) ; The 9-note melody is then processed into several groups of four ; notes which ultimately ‘interleave’ across the larger melody: (setf melody (gen-interleave 4 samples) => ((fs5 f6 cs6 e5) (f6 cs6 e5 fs5) (cs6 e5 fs5 gs5) (e5 fs5 gs5 cs5) (fs5 gs5 cs5 f6) (gs5 cs5 f6 f6)) 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): (setf sect-1a (make-omn :pitch melody :length (gen-loop (length melody) '(e e e q)) ))  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. Appendix A: ‘A Cage Machine’ ; A Simple 'Cage Machine' (init-seed 5826923) ; Generate a pitch chart (defun gen_notes (num_notes) (setf noteseq (gen-eval num_notes '(rnd-pick (gen-integer 0 23)))) (setf noteseq2 (pitch-transpose (rnd-pick (gen-integer -12 12)) (integer-to-pitch noteseq))) (setf chordize_seq (rnd-pick '(0 1))) (if (= chordize_seq 1) (chordize noteseq2) (melodize noteseq2) ) ) (defun pitch_fragment () (setf out (mapcar (function (lambda (x) (gen_notes x))) (filter-remove '0 (append (rnd-number 1 1 3) (rnd-number 2 0 3))))) ) (setf pitch_table (gen-eval 64 '(pitch_fragment))) ; Generate a duration chart (defun get_lengths (num_notes) (setf noteseq (gen-eval num_notes '(rnd-pick '(t t. s s. e e. q)))) ) (defun length_fragment () (setf out (mapcar (function (lambda (x) (get_lengths x ))) (filter-remove '0 (append (rnd-number 1 1 3) (rnd-number 2 0 3))))) ) (setf length_table (gen-eval 32 '(length_fragment))) (setf rest_table (length-invert (gen-eval 32 '(length_fragment)))) (setf duration_table (append length_table rest_table)) ; Generate a dynamics chart (defun get_dyns (num_notes) (setf noteseq (gen-eval num_notes '(rnd-pick '(ppp pp p mf f = fff)))) ) (defun dyn_fragment () (setf out (mapcar (function (lambda (x) (get_dyns x ))) (filter-remove '0 (append (rnd-number 1 1 1) (rnd-number 2 0 1))))) ) (setf dyn_table (gen-eval 64 '(dyn_fragment))) ; Create a piece ; Pick 64 cells from all the charts ; This forms the right-hand of the piano (setf pitch_picks (rnd-sample 64 pitch_table)) (setf duration_picks (rnd-sample 64 duration_table)) (setf dyn_picks (rnd-sample 64 dyn_table)) ; Pick 64 further pitch cells for the left hand of the piano ; This will use the same duration data, only shuffled ; in order to keep bar durations comprehensible (setf pitch_picks2 (pitch-transpose -24 (rnd-sample 64 pitch_table))) ; Create piano parts (setf pno-rh (filter-repeat 1 (make-omn :length (mcflatten duration_picks) :pitch (mcflatten pitch_picks) :velocity (mcflatten dyn_picks) ) :flat t)) (setf pno-lh (filter-repeat 1 (make-omn :length (mcflatten (rnd-order duration_picks)) :pitch (mcflatten pitch_picks2) :velocity (mcflatten dyn_picks) ) :flat t)) ; Score (def-score Cage ( :title "Sound and Silence" :key-signature '(c maj) :time-signature (get-time-signature (mcflatten duration_picks)) :tempo 92 :layout (list (bracket-group (piano-layout 'pt1 'pt2) ))) (pt1 :omn pno-rh :channel 1 :sound 'gm :program 'acoustic-grand-piano) (pt2 :omn pno-lh :channel 2 :sound 'gm :program 'acoustic-grand-piano) ) Appendix B: A Hexadic Composition ; SECTION A: ; These are generic functions and can be copied into your own scripts ; in order to develop your own Hexadic compositions (defun rnd-unique-list (num pitch-list) (mapcar (function (lambda (x) (rnd-unique num x))) pitch-list)) (defun pick-from-list (picks list) (mapcar (function (lambda (x) (nth x list))) picks)) (def-case card-pitches ; Hearts (he) (hek 'e3) (he1 'f3) (he2 'fs3) (he3 'g3) (he4 'gs3) (he5 'a3) (he6 'bb3) (he7 'b3) (he8 'c4) (he9 'cs4) (he10 'd4) (hej 'eb4) (heq 'e4) ; Diamonds (di) (dik 'e4) (di1 'f4) (di2 'fs4) (di3 'g4) (di4 'gs4) (di5 'a4) (di6 'bb4) (di7 'b4) (di8 'c5) (di9 'cs5) (di10 'd5) (dij 'eb5) (diq 'e5) ; Clubs (cl) (clk 'e5) (cl1 'f5) (cl2 'fs5) (cl3 'g5) (cl4 'gs5) (cl5 'a5) (cl6 'bb5) (cl7 'b5) (cl8 'c6) (cl9 'cs6) (cl10 'd6) (clj 'eb6) (clq 'e6) ; Spades (sp) (spk 'e6) (sp1 'f6) (sp2 'fs6) (sp3 'g6) (sp4 'gs6) (sp5 'a6) (sp6 'bb6) (sp7 'b6) (sp8 'c7) (sp9 'cs7) (sp10 'd7) (spj 'eb7) (spq 'e7) ) ; Return 1 for arm, 0 for pole (def-case interval-arm-or-pole ; Hearts (he) (hek 1) (he1 1) (he2 1) (he3 1) (he4 1) (he5 1) (he7 1) (he8 1) (he9 1) (he10 1) (hej 1) (heq 1) ; Diamonds (di) (dik 1) (di1 1) (di2 1) (di3 1) (di4 1) (di5 1) (di7 1) (di8 1) (di9 1) (di10 1) (dij 1) (diq 1) ; Clubs (cl) (clk 0) (cl1 0) (cl2 0) (cl3 0) (cl4 0) (cl5 0) (cl7 0) (cl8 0) (cl9 0) (cl10 0) (clj 0) (clq 0) ; Spades (sp) (spk 0) (sp1 0) (sp2 0) (sp3 0) (sp4 0) (sp5 0) (sp7 0) (sp8 0) (sp9 0) (sp10 0) (spj 0) (spq 0) ) ; a b c d e f ; arms: 0 1 2 3 4 5 (def-case interval-cell-to-arm (0 0) (1 0) (2 0) (3 0) (4 0) (5 0) (6 1) (7 1) (8 1) (9 1) (10 1) (11 1) (12 2) (13 2) (14 2) (15 2) (16 2) (17 2) (18 3) (19 3) (20 3) (21 3) (22 3) (23 3) (24 4) (25 4) (26 4) (27 4) (28 4) (29 4) (he6 1) (di6 1) (cl6 0) (sp6 0) (30 5) (31 5) (32 5) (33 5) (34 5) (35 5) ) ; i ii iii iv v vi ; poles: 6 7 8 9 10 11 (def-case interval-cell-to-pole (0 6) (1 6) (2 6) (3 7) (4 7) (5 7) (6 8) (7 8) (8 8) (9 9) (10 9) (11 9) (12 10) (13 10) (14 10) (15 11) (16 11) (17 11) (18 7) (19 7) (20 7) (21 6) (22 6) (23 6) (24 9) (25 9) (26 9) (27 8) (28 8) (29 8) (30 11) (31 11) (32 11) (33 10) (34 10) (35 10) ) (def-case progression-values ; Hearts (he) (hek 13) (he1 1) (he2 2) (he3 3) (he7 7) (he8 8) (he9 9) (he10 10) ; Diamonds (di) (dik 13) (di1 1) (di2 2) (di3 3) (di7 7) (di8 8) (di9 9) (di10 10) ; Clubs (cl) (clk 13) (cl1 1) (cl2 2) (cl3 3) (cl7 7) (cl8 8) (cl9 9) (cl10 10) ; Spades (sp) (spk 13) (sp1 1) (sp2 2) (sp3 3) (sp7 7) (sp8 8) (sp9 9) (sp10 10) ) (he4 4) (he5 5) (he6 6) (hej 11) (heq 12) (di4 4) (di5 5) (di6 6) (dij 11) (diq 12) (cl4 4) (cl5 5) (cl6 6) (clj 11) (clq 12) (sp4 4) (sp5 5) (sp6 6) (spj 11) (spq 12) (setf basic-deck '(hek he1 he2 he3 he4 he5 he6 he7 he8 he9 he10 hej heq di1 di2 di3 di4 di5 di6 di7 di8 di9 di10 dij diq cl1 cl2 cl3 cl4 cl5 cl6 cl7 cl8 cl9 cl10 clj)) (setf shuffled-deck-c (rnd-order basic-deck)) (setf shuffled-deck-d (gen-divide 6 shuffled-deck-c)) (setf shuffled-deck (gen-divide 6 (card-pitches shuffled-deck-c))) (setf (setf (setf (setf (setf (setf arm-a arm-b arm-c arm-d arm-e arm-f (nth (nth (nth (nth (nth (nth 0 1 2 3 4 5 shuffled-deck)) shuffled-deck)) shuffled-deck)) shuffled-deck)) shuffled-deck)) shuffled-deck)) (setf (setf (setf (setf (setf (setf pole-i (append (filter-first 3 pole-ii (append (filter-last 3 pole-iii (append (filter-first pole-iv (append (filter-last 3 pole-v (append (filter-first 3 pole-vi (append (filter-last 3 arm-a) (filter-last 3 arm-d))) arm-a) (filter-first 3 arm-d))) 3 arm-b) (filter-last 3 arm-e))) arm-b) (filter-first 3 arm-e))) arm-c) (filter-last 3 arm-f))) arm-c) (filter-first 3 arm-f))) (setf hexafields (sort-asc (append (list arm-a) (list arm-b) (list arm-c) (list arm-d) (list arm-e) (list arm-f) (list pole-i) (list pole-ii) (list pole-iii) (list pole-iv) (list pole-v) (list pole-vi) ))) (setf centre-tones (sort-asc (pitch-transpose-n '(0 -12 -24 -36 -48) (gen-repeat 5 (card-pitches '(sp3)))))) (setf hexafield-progressions '()) (setf interval-card-progressions '()) (setf interval-card '(cl3)) (setf interval-cell (list (position (car (card-pitches interval-card)) (flatten shuffled-deck)))) (setf arm-or-pole (interval-arm-or-pole interval-card)) (def-case assign-hexafield (0 (setf current-hexafield-n (interval-cell-to-pole interval-cell))) (1 (setf current-hexafield-n (interval-cell-to-arm interval-cell))) ) (assign-hexafield arm-or-pole) (setf current-hexafield (nth (car current-hexafield-n) hexafields)) (setf hexafield-progressions (append hexafield-progressions (list current-hexafield))) (setf interval-card-progressions (append interval-card-progressions (card-pitches interval-card))) (setf progression-interval (progression-values interval-card)) (setf next-progression-cell (mod (+ (car interval-cell) (car progressioninterval)) 36)) (defun next-progression () (setf interval-card (list (nth next-progression-cell shuffled-deckc))) (setf interval-cell (list (position (car (card-pitches interval-card)) (flatten shuffled-deck)))) (setf arm-or-pole (interval-arm-or-pole interval-card)) (assign-hexafield arm-or-pole) (setf current-hexafield (nth (car current-hexafield-n) hexafields)) (setf hexafield-progressions (append hexafield-progressions (list current-hexafield))) (setf interval-card-progressions (append interval-card-progressions (card-pitches intervalcard))) (setf progression-interval (progression-values interval-card)) (setf next-progression-cell (mod (+ (car interval-cell) (car progression-interval)) 36)) ) (gen-eval 5 '(next-progression)) (setf progressions-centres (sort-asc (gen-mix (gen-repeat 12 (list centre-tones)) hexafield-progressions))) ; SECTION B: ; This is the compositional section, unique for each Hexadic composition ; Compose with progression 1 ; Get ; (setf (setf (setf (setf (setf interval tone, hexafield and interval tone number (0 will be root) progress-marker 0) itone (nth progress-marker interval-card-progressions)) hexaf-c (nth progress-marker progressions-centres)) hexaf (sort-asc (nth progress-marker hexafield-progressions))) itone-num (position itone hexaf)) (setf p1 (append (nth 0 (nth 0 (nth 1 (nth 2 ) ) (list hexaf) hexaf) hexaf) hexaf)) (setf p2 (append itone (nth 3 (nth 4 (nth 5 (nth 4 (nth 3 ) )) (list hexaf) hexaf) hexaf) hexaf) hexaf) (setf p3 (append (list (nth 0 centre-tones) ) ) ) ; Codify as a function (defun nextsequence () (setf itone (nth progress-marker interval-card-progressions)) (setf hexaf-c (nth progress-marker progressions-centres)) (setf hexaf (sort-asc (nth progress-marker hexafield-progressions))) (setf itone-num (position itone hexaf)) (setf p1 (append (nth 0 (nth 0 (nth 1 (nth 2 p1 (list hexaf) hexaf) hexaf) hexaf)) ) ) (setf p2 (append p2 (list itone (nth 3 hexaf) (nth 4 hexaf) (nth 5 hexaf) (nth 4 hexaf) (nth (rnd-pick '(3 5)) hexaf) ) )) (setf p3 (append p3 (list (nth 0 centre-tones) ) ) ) ) (setf progress-marker (nextsequence) (setf progress-marker (nextsequence) (setf progress-marker (nextsequence) (setf progress-marker (nextsequence) (setf progress-marker (nextsequence) 1) 2) 3) 4) 5) (setf r1 (gen-repeat (/ (length p1) 4) '(-1/2 1/2 1/2 1/2 1/2 -1/2))) (setf r2 (append (gen-repeat (/ (length p2) 6) '(-1/2 1/2 1/4 1/4 1/2 1/4 1/4 -1/2)))) (setf r3 (gen-repeat (length p3) '(2/1_1/1))) (setf o1 (make-omn :pitch p1 :length r1)) (setf o2 (make-omn :pitch p2 :length r2)) (setf o3 (make-omn :pitch p3 :length r3)) (def-score Hexadic (:key-signature '(c maj) :time-signature '(6 2) :tempo 64 :layout (list (bracket-group (piano-layout 'pt1 'pt2) (bass-down8-layout 'bass) ))) (pt1 :omn o2 :channel 1 :sound 'gm :program 'acoustic-grand-piano) (pt2 :omn o1 :channel 2 :sound 'gm :program 'acoustic-grand-piano) (bass :omn (pitch-transpose -12 o3) :channel 3 :sound 'gm :program 'acoustic-grand-piano) )