Demonstration 10 provides the practical component for Chapter 12: “Comparative Search” of my unpublished textbook on composing programs. It illustrates how comparative searches can be used to compose a piece of music, and in doing so, illustrates the principle of bottom-up design. The technique here called “comparative search”1 comes directly from Artificial Intelligence, where it is known as “alpha-beta pruning”.
As contrasted with the top-down deductive approach used to generate Demonstration 8 and Demonstration 9, the form of Demonstration 10 is induced from the bottom up, on the basis of qualities inherent in pre-composed material. The approach reflects the author's composition Protocol to the extent that Demonstration 10's material consists of recurrent modules, each comprised of several chords, and to the extent also that the order of progression is contextually determined. Like Demonstration 7, Demonstration 10 employs arpeggiation to adapt this chordal material to the monophonic nature of the clarinet.
The process of composing Demonstration 10 divides into three stages of production:
Figure 1 shows how the material for Demonstration 10 is organized. The material will consist of twelve chords grouped into three modules. Each module is described by a characteristic register, by a range of articulations, and by an average chordal duration. Dots on the registral graphs indicate center pitches of a five-semitone gamut. The bold vertical lines on the graph of articulations define regions of relative likelihood for three styles of playing: (1) slurred, (2) normal, and (3) detached. The average chordal duration supplied for each module indicates sixteenth notes.
Stage I of the composing process seeks to reconcile directives intended to promote chromatic diversity, dissonance, and divergent part-leading. The most emphatic of these directives assume the status of constraints:
These constraints, along with the heuristic criteria described below, are formulated with the specific rhetoric of Demonstration 10 in mind. The purpose here is in no way to promote stylistic principles, but rather to illustrate how assertions of this kind can be implemented programmatically.
Each chord will be composed using a comparative search; backtracking is limited to the extent that once the program advances to a new chord, no revision of any previous chord may happen. Subject to the constraints listed above, each search employs two criteria for evaluating candidate chords.
Understand that a comparative search prunes fruitless solution paths whenever it deems the current partial candidate less satisfactory than any complete solution already discovered. This feature requires criteria to be expressed in a contrary way, as practices to avoid rather than practices to encourage. Thus the present search seeks to discourage redundancy rather than encourage diversity; likewise it seeks to discourage consonances rather than encourage dissonances. The prioritization of criteria works out as follows: less chromatically redundant candidates displace incumbents; more redundant candidates are discarded. When a candidate and an incumbent have equal chromatic redundancy, then the candidate displaces the incumbent only when the candidate has fewer consonances.
Figure 2 shows the results of the twelve chordal searches. The two key values supplied beneath each chord indicate (1) chromatic redundancy relative to the proceeding chords and (2) sonorous quality expressed as packed tallies of intervallic types, with the greater significances associated with the more consonant intervals. Notice that the formulation of ‘sonorous quality’ favors chords of the diminished seventh (chords 2 and 4) over chords of superimposed fourths, fifths, and tritones (especially chord 8, but see also chords 7 and 11).
With 5 pitches available to each part and 4 parts, the theoretic total of candidate chords for each search is 54=625. In practice, the constraints against doublings, parallelism, and chromatic overlap substantially reduce the number of acceptable chords. Only when a partial chord meets the absolute requirements imposed by these constraints does a search proceed to evaluate its chromatic redundancy and sonorous quality.
Demonstration 10 divides into twelve segments, each of which draws material from a single module. Table 1 lists segment attributes. The number of chords appearing in a segment depend both upon the length of the segment and upon the module's average chordal duration.
The column labeled “Possible Arrangements” gives numbers calculated as
N factorial, where
comes from the column labeled “Chordal Positions”. Since
N factorial counts how many ways
different items can be arranged, this number is overestimates arrangements. However the purpose in supplying this number was to verify that the search
space space will be reasonable for a comparative search, and that purpose is adequately served.
The chordal content of each segment was itself derived programmatically. Notice that Table 1 allocates a total of 5+4+3+3=15 positions for module 1, 6+6+5+5=22 positions for module 2, and 3+4+4+6=17 positions for module 3. With these position counts it was not possible to represent every chord an equal number of times, yet the chord counts are close to uniform: 5+5+5=15 for module 1, 4+5+4+4+5=22 for module 2, and 5+4+4+4=17 for module 3. Which chords went with which segment was determined by random shuffling.
Given the chordal content of a segment, Stage II of the composing process is responsible for determining in what order the chords will occur. This problem is fundamentally one of organizing a statistical frame; where programs for earlier Demonstrations would have either shuffled the chords randomly (Demonstration 3) or sorted the chords so that preferred chords would be placed in the most desirable positions (Demonstration 7), the technique of comparative search sensitizes the current program to context when it considers alternate chordal progressions.
By contrast to the chord-composing searches described above, the searches for ‘optimal’ chordal progressions impose no constraints. The heuristic criteria are once again expressed in a contrary way so that minimax comparisons can be used to prune unfruitful search paths. There are two of these criteria:
The goal of the search is to arrange the chords in such a manner that blandness will be minimal. Given progressions of equal blandness, a subsidiary goal is to arrange the chords so as to assign longer durations to those chords which occur least often in a segment. Random shuffling insures unbiased selection between progressions which the program otherwise judges equally suitable.
The result of the search appears in Figure 3. Modules and chord numbers refer back to Figure 1 and Figure 2. The blandness given for each segment reflects pairs of consecutive chords sharing one or more common chromatic degrees.
The transcribed product appears in Figure 4.
The following explanations seek to illustrate comparative searches in play. The explanations are peppered with line numbers, but you are are by no means expected to chase down every line of code. Rather, you should follow through with line numbers only when you have a specific question that the narrative is not answering.
DEMO10 proper appears as Listing 1; the
routine used to initialize
COMMON memory variables appears as Listing 2.
The body of program
DEMO10 simply calls out to three subroutines, each of which implements a stage
The call to subroutine CHORDS in line 11 invokes Stage I, which generates the
The call to subroutine PROGRS in line 12 invokes Stage II, which fleshes out
the mid-level form by determining which chord should follow which.
The call to subroutine ARPEG
Listing 1: Program
Since several items of information are necessary to describe each option selected by a decision, these items are organized into parallel stacks accessed by a single index.
The symbols shared between program
DEMO10 and its subroutines adhere to five root abbreviations pertaining
to the various categories of information in the musical design:
CHDsignifies information pertaining to entire chords. The parameter MCHD gives the total number of chords in Figure 1, while the variable
ICHDindicates one specific chord at a time. In subroutine
CHORDS, array element
MODCHD(I)indicates which module contains the
PRTsignifies information pertaining to individual parts within a chord. The parameter
MPRTgives the number of parts per chord in Figure 1, while the recursive index
IPRTindicates one specific part within one specific chord. Array element
PCHPRT(I,J)gives the pitch selected by
Ith part of the
Jth chord, while the element
DEGPRT(I,J)gives the corresponding chromatic degree.
SEGsignifies information pertaining to individual segments. The parameter
MSEGgives the total count of segments in Table 1, while the variable
ISEGindicates the segment currently being composed. Array element
NUMSEG(I)gives the number of chordal positions from column 5 of Table 1; array element
PRGSEG(I)indicates the first position in the progression occurring in the
PRGsignifies information pertaining to the progression of chords. The parameter
MPRGgives the total number number of chords in all segments combined, which is the sum of column 5 of Table 1. The recursive index
IPRGindicates specific positions in the progression. Array element
DURPRG(I)gives the duration allotted to the
Ith position; array element
CHDPRG(I)indicates which of the 12 chords has been selected by subroutine
PROGRSfor this position.
POLPRGcontains pools of chords to be used in each segment.
CHORDS is listed in Listing 3 (a) and Listing 3 (b).
This subroutine implements Stage I of compositional processing, which selects pitches for chords.
It uses raw material from array
REGPRT to populate
chordal content into arrays
Listing 3 (a): Subroutine
Listing 3 (b): Subroutine
The outermost loop of subroutine
CHORDS (lines 18-115) iterates on the variable
ICHD, so as to initiate
comparative searches for each of the 12 chords.
The next-to-outermost loop (lines 23-114) implements the recursive mechanism for each search. The level of recursion is tracked by the
IPRT, which advances and retreats in the time dimension.
The heart of the chord-generating process is the recursive mechanism implemented as the next-to-outermost loop from line 23 to line 114. Like the recursive implementations for Demonstration 8 Demonstration 9, the loop indexing here is bi-directional:
IPRTiterates forward and back from low register to high register, where
IPRT=1is the lowest register in the chord.
IPRGiterates from 1 to
MPCHand tracks the option under consideration for the current part. Each change in
IPRTdisrupts the forward progression of
IDX; therefore, array
IDXPRTkeeps track of
IPRTvalues. Thus whenever the recursive loop advances to the next part, it pushes the value of
IDXPRT(IPRT)(line 28). Conversely when the recursive loop retreats to the previous part, it pops the value of
Each loop iteration begins first by advancing
IDX one position to the right (line 26), then
immediately testing (line 27) whether
IDX has reached its limit.
The process advances to the next (higher) part in line 98 and immediately thereafter initializes
COMPOS retreats to the previous (lower) part in line 105, but the value of
IDX is not reclaimed
until the loop iterates (line 26).
Options for parts in the chordal search combine the registral locus stored in array element
(initialized in lines 14-17) along with displacements stored in array element
These displacements range from 0 to 2 semitones above and below the locus (line 11). From the locus and displacement,
CHORDS computes a pitch (line 29) and a degree (line 32). Arrays
store these values temporarily until such tie as
CHORDS determines whether the current candidate compares favorably
or not to the incumbent. In the former case,
CHORDS transfers values from
to more permanent residence in
DEGPRT, at least until a better chord is encountered.
The first thing
CHORDS does when considering an option is to check for doublings (lines 33-35), parallelism (lines 39043
along with the logical function
PARALL listed in Listing 4) and chromatic overlap (lines
46-47). Only when these constraints are satisfied does
CHORDS attempt any heuristic evaluation.
Evaluation of chromatic redundancy employs arrays
USEDEG(I) tallies the number of already-composed chords employing the
Ith degree of the chromatic scale,
while array element
USETMP(J) holds the largest of these tallies for the first J degrees in the candidate.
CHORDS evaluates the chromatic redundancy of each partial candidate (lines s61-66) by selecting the maximum
between the current degree's tally and largest value for all previous parts; the variable
LUSE gives the
chromatic redundancy of the incumbent.
Evaluation of sonorous quality proceeds only when the candidate is no more redundant than the incumbent.
QALTVL (initialized in line 12) indicates the relative significances for each type of interval from the semitone to the major
CHORDS considers only intervals between degrees, without regard to inversion or octave displacement (lines 77-79).
Since four-part chords are characterized by 6 intervals, significances in
QALTVL are expressed as powers of 7.
QUALTMP(I) gives the contributions to the candidate of all intervals resulting from the first
I degrees, while the variable
LAQAL gives the sonorous quality of the incumbent.
PROGRS is listed in Listing 5 (a) and Listing 5 (b). This subroutine
implements Stage II of compositional processing, which arranges chords within segments.
It uses raw material from array
POLPRG along with processed material populated into arrays
PROGRS in turn populates
chordal content into arrays
Listing 5 (a): Subroutine
Listing 5 (b): Subroutine
PROGRS's main loop (lines 31-116) iterates on the variable
ISEG, so as to initiate comparative
searches for each of the 12 segments listed in Table 1.
PROGRS draws its raw material from array
The contents of this array follow Table 1 and are initialized in line 13 of the
BLOCK DATA routine.
Chords are listed in the following way: for the
Ith segment of the piece, set
the array elements
POLCHD(IPRG+NUM-1) hold the appropriate pool of chords.
The heart of the chord-arranging process is the recursive mechanism implemented as the next-to-outermost loop from line 41 to line 115 The loop indexing is bi-directional:
ITMPiterates forward and back through the current segment's chordal positions, where
ITMP=1is the segment's leftmost position.
ITMPranges from 1 to
NUMSEG(ISEG). The variable
LPRGindicates the position globally. It ranges from
PRGTMP(ITMP)gives the index to the option under consideration for position
ITMP. In this case, options are drawn with regard to order but without replacement from the pool of chords stored in array elements
POLPRG(PRGTMP(ITMP)) temporarily until such time as
determines whether the candidate progression compares favorably or not to the incumbent.
CHDTMP's content is transferred to more permanent
CHDPRG, at least until a better progression is encountered.
Prior to its first search
PROGRS analyzes each pair of chords for common chromatic degrees (lines 14-28).
Upon completion of this analysis, array element
BLNCHD(I,J) gives the number of degrees shared in common by the
Jth chords. We shall take this number as reflecting the relative blandness of progressing
directly from chord
I to chord
J. In deriving compound blandnesses for progressions of
PROGRS assigns greater significance to pairs sharing more degrees in common.
Since the maximum number of chords which
PROGRS must arrange within any single segment is s6, significances of individual
blandnesses are expressed as powers of 7 (lines 54-60). Array element
BLNTMP(K) gives the blandness
of the first
K chords currently under consideration for the segment, while the variable
LBN gives the
blandness of the incumbent progression.
The method of evaluating statistical balance between the chords is intriguing because the program consults several values whose relative significances
vary according to context. The values themselves are cumulative durations stored in array
USETMP (this array bears no relation to the
USETMP employed in subroutine
CHORDS). Each time
PROGRS selects a chord for a position,
PROGRS augments the appropriate element of
USETMP by the position's duration (line 48); conversely,
USETMP whenever it discards a chord (line 104). Array
SCDTMP holds indices
to each element of
USETMP; a call in line 67 to the library subroutine
sorts the contents of
in descending order according to the contents of
USETMP. This awards greatest significance to the largest cumulative durations.
SCDCHD hold cumulative durations and an associated schedule for the incumbent. Usage comparisons between candidates
and incumbents are undertaken only when the program judges both progressions to be equally bland.
implements such comparisons by stepping through pairs of duration in order of significance until it finds a pair which differs. It then favors the chord
(candidate or incumbent) with the smaller duration (lines 68-78).
ARPEGG very closely resembles its counterpart in program
DEMO7, this subroutine is not
|© Charles Ames||Original Text: 1984-11-01||Page created: 2017-03-12||Last updated: 2017-03-12|