In the index/supply design, the active component generates intermediate index values in the discrete range from 0 to N-1. The passive component uses the intermediate index to look up a target value in the supply containing N elements. When I use the word "selection" here, its about dereferencing a supply element using a generated index. A supply might be implemented as a simple array or as a list (an indexed collection).
My own experience with the index/supply design comes from a program named Compose
, which I developed during my two-semester teaching career.
(The first semester happened at the New England Conservatory and the second semester happened
at the State University of New York at Buffalo.)
Like Koenig's Project2
, which was very much in mind, Compose
was created to give non-programmers an opportunity to mix and match compositional algorithms. Compose
was programmed for the
Macintosh Plus and Macintosh II and as such
represented a generational upgrade of Project2
to a windowing environment. Compose
projects were
built up from procedural units, each unit being presented as a modeless dialog. Some units implemented
selection principles like Koenig's SEQUENCE and SERIES. Other units collated time, duration, timbre, dynamic, and pitch to create elemental
subscores (single notes and/or chords). Still other units assembled smaller subscores into larger ones. The fully assembled score could
then be performed using MIDI. The semester at SUNY/Buffalo ended with a concert of music by graduate composition students, with 11 of the
12 pieces being produced using Compose
. I gave everybody A's, even the guy who didn't use my program.
Compose
was subsequently documented in Interface: Journal of New Music
Research^{1}.
It organized all its data (integers, reals, degrees, pitches, and subscores) as trees. Trees could be single elements
(effectively a single-member list), lists of elements, or lists of subtrees. A lot of what went on in a Compose
project involved assembling smaller trees
into larger trees and/or selecting subtrees from a tree's top-level list. Thus a phrase-generating unit could append subscores acquired from a note-generating unit, and
the note-generating unit could in turn acquire pitches from a sequence-iterating unit. The sequence-iterating unit would have for its
supply a tree of pitches (each element being a pitch or a chord). The sequence-iterating unit would also be responsible for notifying the phrase-generating unit when its
supply had been exhausted.
The index generators (indexers for short) described on this site were all originally features of Compose
. They are:
IndexSequence
(there's also a DriverSequence
) draws indices sequentially from a stored collection of integers.
Feedback
draws from the discrete range from 0
to N-1 using a quasi-deterministic algorithm favoring indices which have so-far received less than their
fair share of usage.
MarkovMatrix
conditions each target selection upon its source — that is,
the selection that came before.
ChomskyGrammar
conditions each selection both on the choice that came before and
the choice that is coming up.
Couplings which process continuous Driver
output from zero to unity through bounded-range
Transform.Discrete
units can also serve the
Indexer
role.
In the unpublished textbook I wrote on composing programs (reproduced on this site here) the Index/Supply pattern contributed to the pedagogical organization, as did Jakob Bernolli's urn model and Noam Chomky's phrase-structure grammars:
IndexSequence
in
UsageMode.SAMPLES_DIRECT
.
IndexSequence
in
UsageMode.DIRECT_SHUFFLED
.
This is a rudimentary kind of context sensitivity, governed by a rule against repeating any supply element until the entire supply had
been presented.
MarkovMatrix
indexer.
In the phrase-structure world Markov chains reflect sensitivity to what came before, which limitation was criticized by Chomsky for having no
sensitivity to what was to come.
Feedback
indexer.
ChomskyGrammar
indexer was a feature of Compose
intended to give non-programmers the opportunity to design such productions. No students actually used it.
Later chapters of my textbook (14 chapters in all) veered away from sequence generation, turning rather to the constrained decision-making algorithms which I favor in my own composing programs.
Isolating the supply from the selection principle detaches the supply element from the index used to select it.
This detachment creates no issues for indexers which are neutral; that is, where indices have equal statistical weight and where the
only context is the element's position in the supply. Such is the case with IndexSequence
.
However the remaining indexers are less neutral. Index-weights are normally unequal for applications employing Feedback
.
This situation leaves it to the user to make sure index weights align properly with supply elements.
Context issues become even more intense with
MarkovMatrix
and
ChomskyGrammar
.
Within the Index/Supply design, looking up a value in an array or list establishes a
mathematical function mapping values from a source set, the
domain, to a target set, which in my day was called the
range.
I have adopted the term index domain
for the source set, since this set describes values produced generally by Indexer.next()
.
Mathematically, index domain values are whole numbers. Whole
numbers are "discrete" in the sense that each given number has just one immediate successor; the number half-way
between is not also a whole number. This means that whole numbers
can be represented in binary form without digits to the right of the binary point.
Whole numbers are thus distinguished from real numbers which are continuous and which have digits
right of the binary point. Whole numbers are implemented on this site using Java's int
type.
Each supply defines its own index domain. That particular domain runs from 0 to N-1, where N is the number of supply elements. The supply size N is the number returned by Indexer.getLimit().
Indexer
interface.
No interface is required to support the supply component of an Index/Supply coupling, since indexed lookup is already a fundamental
usage pattern for all programming languages. Either an array or a
java.util.List
will
do nicely for a supply.
The Indexer
interface presented as Listing 1 prescribes
common features for index generators. The all-important
next()
and hasNext()
methods
are brought in from java.lang.Iterator
;
each next()
call
returning an Integer
.
The interface also provides boolean
valueInRange()
and exception-throwing checkIndexValue()
methods to enforce holding indices to the
range from 0 (inclusive) to limit
(exclusive).
© Charles Ames | Page created: 2022-08-29 | Last updated: 2022-08-29 |