• No results found

A Complete Example

In document Algorithmic Composition (Page 83-89)

Algorithmic Composition

5.4 A Complete Example

Example 5.4.1 uses score-merge to combine 6 scores. The merge is preceded by 6 generators producing scores 1, 2, 3a, 3b, 3c, and 3d.

The merge is based on the octatonic scale, initially presented in

5.4 A Complete Example 69

ries. The octatonic scale is a series of eight pitches that alternate whole and half steps for one octave. Permutations of the octatonic scale form the pitch material of the remaining generators as a means of creating pitch homogeneity.

Example 5.4.1: my-first-merge.sal

; Compute and merge 6 different scores:

; 1, 2, 3a, 3b, 3c and 3d.

; Score-1 is the "melody", derived from an octatonic

; scale emitted by a palindrome pattern.

set pitch-1 = make-palindrome(

list(ef3, e3, fs3, g3, a3, bf3, c4, cs4)) set rhythm-1 = make-accumulation(

{.22 .23 .25 .26 .28 .29 .31}) define function score-1-helper(count)

set vel-1 = interpolate(count, 0, 40, 47, 125), ioi-1 = next(rhythm-1)

exec score-gen(save: quote(score-1), score-len: 48, pre: score-1-helper(sg:count), pitch: next(pitch-1),

ioi: ioi-1, vel: vel-1,

dur: vel-1 * 0.01 * ioi-1)

; Score-2: chords that punctuate the melodic material set pitch-2 = make-random(

list(list(list(c2, d3, ef4, f5)), list(nil),

list(list(cs1, ds2, e3, fs4)), list(gs5)))

set rhythm-2 = make-random(

list(i, id, list(s, keyword(weight), .5), sd)) set vel-2 = make-heap({80 90 100})

define function score-2-helper() set vel-2-item = next(vel-2)

exec score-gen(save: quote(score-2), begin: 7, score-dur: 4.5,

pre: score-2-helper(), pitch: next(pitch-2), ioi: next(rhythm-2), vel: vel-2-item,

dur: vel-2-item * 0.005)

; Scores 3a-3d: an accompaniment to pitch material set pitch-3a = make-cycle(list(c4, d5, ef6, f6)) set rhythm-3a = make-heap({0.01 0.035 0.048}) exec score-gen(save: quote(score-3a), begin: 2.8, score-len: 4,

pitch: next(pitch-3a),

vel: interpolate(sg:count, 0, 60, 3, 80), ioi: next(rhythm-3a),

dur: 0.1)

set pitch-3b = make-cycle(list(cs3, ds4, e5, fs6, gs7)) set rhythm-3b = make-heap({0.01 0.035 0.048})

exec score-gen(save: quote(score-3b), begin: 3.75, score-len: 5,

pitch: next(pitch-3b),

vel: interpolate(sg:count, 0, 60, 4, 80), ioi: next(rhythm-3b),

dur: 0.1)

set pitch-3c = make-cycle(list(c4, d5, ef6, f6)) set rhythm-3c = make-heap({0.01 0.035 0.048}) exec score-gen(save: quote(score-3c), begin: 4.1, score-len: 4,

set pitch-3d = make-cycle(list(list(cs2, ds3, e4), nil, exec score-gen(save: quote(score-3d), begin: 4.1, score-len: 6,

set the-score = score-merge(score-1, score-2, score-3a, score-3b, score-3c, score-3d)

Notice how each score is generated: first, various pattern objects are created and assigned to global variables. Variable names are

5.4 A Complete Example 71

sen to avoid duplication. Trying to use the same variable for two dif-ferent purposes is called “name collision,” and is one of the hazards of using global variables. There are more sophisticated programming techniques to avoid this problem, but for now, we will take a simple approach and just be careful with name choices.

After creating the pattern objects to be used, score-gen is used to create a score. Again, global variables retain the scores (the save:

keyword provides the variable name). At the very end, score-merge is used to combine all the score parts into one score.

Most note parameters such as pitch: are generated independently with the expression next(pattern-object), which just takes the next item from the pattern. In this example, ioi: and dur: in score-1 de-pend on the value of next(rhythm-1), but if we simply evaluated this expression twice (once for ioi: and once for dur:) we would get two different items from the rhythm-1 pattern. Instead, we evaluate next(rhythm-1) just once per generated note and use set to store the value in the variable ioi-1. Then, we use ioi-1 in two different ex-pressions to specify the ioi: and dur: parameters. Similarly, dur: de-pends on vel:, so we introduce the variable vel-1 and use it in two places.

To compute ioi-1 and vel-1 once for every note, we use the pre:

keyword. The expression following pre: is evaluated once before each note. (There is also an optional post: keyword to evaluate an expression after each note.) Notice that pre: (and all keywords) must be followed by an expression, but set is a command, so we cannot say something like pre: set ioi-1 = next(rhythm-1). However, we can call a function, so we define score-1-helper to do the assign-ments. Again, there are more sophisticated ways to do this, but we opt for the approach that uses concepts we have already seen.

Now that we understand how the score-1 generator is structured, let us look more closely at what it computes. The rhythm-1 pattern is the accumulate pattern applied to a list of durations. It returns the sequence .22, .22, .23, .22, .23, .25, .22, .23, .25, .26, etc. score-1 will have a length of 48 notes, as indicated by the :score-len pa-rameter.

The variable vel-1 is computed by

set vel-1 = interpolate(sg:count, 0, 40, 47, 125) The interpolate function has parameters x, x1, y1, x2, y2. It com-putes the y value at x on the line from point (x1, y1) to (x2, y2). In this case, sg:count is an index that starts at 0 and increments after each note is generated. Thus, sg:count takes on the values 0 through 47 for the 48 notes of score-1. The remaining parameters say that when sg:count is 0, the output (y) should be 40, and when sg:count

is 47, the output should be 125. The output is interpolated (or ex-trapolated) for other values of sg:count. Thus, the result values will increase smoothly from 40 to 125 over the 48 notes in score-1. The variable ioi-1 is simply the next item from the rhythm-1 pattern.

The ioi: and vel: parameters are specified by very simple expres-sions: just an unquoted symbol. Recall that a symbol evaluates to the value of the indicated variable. The dur: expression scales the dura-tion by the velocity so that louder notes are also longer. Since veloc-ity values range (normally) from 1 to 127, an extra scale factor of 0.01 is used so that the scale factor is between 0.01 and 1.27.

score-2 uses chords. Notice how the pattern object for pitch (stored in the variable pitch-2) is created. make-random chooses between items in a list, and in this case, the items themselves are lists: (c2, d3, ef4, f5), nil, (cs1, ds2, e3, fs4), and gs5. You might think the input to make-random should therefore be something like {{c2 d3 ef4 f5} {} {cs1 ds2 e3 fs4} gs5}; however, when make-random sees a list as an item, it expects to find an item fol-lowed by keywords weight:, min:, or max:. To tell make-random that the items themselves are lists, an additional level of nesting must be used, so the input is actually {{{c2 d3 ef4 f5}} {{}} {{cs1 ds2 e3 fs4}} gs5}. Instead of braces {}, the program uses list to construct lists so that the pitch names, which are global variables, will be evaluated to produce numbers. (Note that {c4} is a list con-taining the symbol c4, whereas list(c4) is equivalent to {60}.)

To finish this discussion on lists of pitches, remember that when a score is played, if the pitch: keyword contains a list, the note expres-sion is expanded into one note for each element of the list. Thus, the generated “notes” will be chords. The empty list “expands” into no notes, so it is effectively a rest.

Previously, it was stated that the parameter expressions are evalu-ated once for each note. To be more precise, we should say the ex-pressions are evaluated once for each score event, which may be a note, a chord, or a rest. Similarly, the score-len: parameter is the number of score events, so chords and rests count the same as a sin-gle note.

To listen to the-score, use jnyqide to open “my-first-merge.sal” and push the load button. To see the score, type exec score-print(the-score), or to hear the score, type exec score-play(the-score).

5.5 Suggested Listening 73

5.5 Suggested Listening

“U” (The Cormorant) for violin, computer, and quadraphonic sound composed by Mari Kimura is motivated by the blight of the oil-cov-ered cormorants in the Persian Gulf. The formal structure of the composition is quasi-palindromic, imitating the shape of the letter

“U” (Kimura, 1992).

Phasing by John Woodruff is included in the electronic materials in its original version for Common Lisp (phasing-woodruff.lsp), translated to SAL (phasing-woodruff.sal), and as a sound file (phasing.wav).

Chapter 6 Printing, Reading, and

In document Algorithmic Composition (Page 83-89)