Algorithmic Composition: A Gentle Introduction to Music Composition Using Common LISP and Common Music
Skip other details (including permanent urls, DOI, citation information) :This work is protected by copyright and may be linked to without seeking permission. Permission must be received for subsequent distribution in print or electronically. Please contact : [email protected] for more information.
For more information, read Michigan Publishing's access and usage policy.
Chapter 5: Introduction to Algorithmic Composition
In Chapter 4, we learned how to create instances of objects and store those objects in a container in the Common Music class hierarchy using the Stella command interpreter. In this chapter, we will learn how to create containers and assign its slots by describing writing programs. Section 4.3 is helpful when first getting acquainted with the Common Music object system and some of the more frequently used commands. More powerful use of the system comes from writing programs.
5.1 Getting Started
Perhaps the easiest way to write a program is to type code into a file. Consult your Common LISP documentation to learn how to create, edit, save, compile, and load files.
Before we begin entering code into a file, we must learn how Common Music assigns values to slots. In section 4.4, we learned how to assign values to global variables using SETF. Common Music also uses SETF to assign values to slots. Example 5.1.1 assigns the MIDI note slot a value of 60 with an amplitude of .5.
Example 5.1.1
Example 5.1.2 shows how you can write a program to create a generator and place a note in that generator.
Example 5.1.2: my-first-generator.lisp
Note: examples followed by : and a filename as in Example 5.1.2 are included on the accompanying compact disc.
What does this code say? We create an instance of a generator called my-first-generator and we fill the generator with midi-note objects. We can initialize the generator's slots in the parentheses following the instantiation of the generator. In this case, we initialize the generator's length slot to have a value of one midi-note. Following the container initialization list, we enter the body of the generator where the additional values are bound to slots using SETF. Notice that SETF is used with slot name-slot value pairs enclosed in parentheses.my-first-generator is closed by a concluding right parenthesis that balances with the left parenthesis that initiated the program.
What are the various container initialization slots? The container object has optional initialization parameters for start time in seconds. Because of Common Music's object hierarchy, thread, merge, and algorithm inherit start from container. In addition, algorithm has container initializations for length, count, and end.length is the number of note or rest events in the container.count is a Common Music variable that increments its value based on the number of note or rest events in a container.end specifies the ending time in seconds.heap inherits from thread so heap has an optional initialization for start time.generator inherits from thread and algorithm so generator has optional initializations for start, length, count, and end.mute inherits from algorithm so mute also has optional initializations for start, length, count, and end.
Once the code has been typed into a file, save the file as my-first-generator.lisp. The .lisp extension will help you recognize the file as Common Music source code.
Next, evaluate the code by selecting all of the text in my-first-generator.lisp, copying it, and then pasting it into Stella. Press return and Stella will evaluate the program and create my-first-generator. Alternatively, you may also evaluate the code by loading the file into Common Music
You may listen to the generator by entering the Common Music command mix my-first-generator at Stella's Top-Level or change the focus object to my-first-generator and enter the command mix.
The result of the Common Music evaluation may be saved as a Common Music file using the Common Music command archive command. Notice that in Example 5.1.3, the focus object is my-first-generator. Using a file extension of .cm identifies the file as a Common Music archive.
Just as with my-first-generator.lisp, you may load my-first-generator.cm and Common Music will load and evaluate the file.
What's the difference between the Common Music .lisp source file and the Common Music .cm archived file? Open and view the contents of my-first-generator.cm. You'll see the code that Common Music generated when it evaluated my-first-generator.lisp.
In addition to saving the .lisp or .cm source, you may wish to save the musical output from my-first-generator.lisp to a MIDI file (.mid). Saving the output of Common Music as a standard MIDI file means you can readily combine the power of Common Music with the functionality of a MIDI sequencer. Type the Common Music command, open <filename>.mid to open a stream to a MIDI file. Once the stream is open, mix the container. The output of the container is directed to <filename>.mid. When you're finished writing the file, use the Common Music command close to close the stream.
Stella [My-First-Generator]: close my-first-generator.mid.copy
Stella [My-First-Generator]:
If you're using Common Music on a Macintosh, from the Common Music menu, select Streams , New Streams , and MIDI file . Common Music will bring up a window with a default file name in the title bar of the window. You can enter attributes of the .mid file such as the filename and start time. Use the mix command to route the MIDI output to the named .mid file. To redirect MIDI output to the port, from the Common Music menu, select Streams , and MIDI .
Table 5.1.1 gives an overview of the file formats discussed in Section 5.1.
Table 5.1.1
File Type | File Suffix | Description | Command(s) associated with file creation |
Common Music LISP source | .lisp | File contains Common Music and Common LISP program code | Refer to your Common LISP documentation on how to create, edit, save, compile, and load files. |
Common Music Archive | .cm | File contains CLOS program code | archive |
MIDI File | .mid | File contains MIDI data created as a result of opening a stream for MIDI output | open mix |
5.2 Item Streams
In order to do something musically interesting, we certainly need to produce containers that have more than one note! The easiest way to create containers that have more than one note is to use item streams . Item streams are an ordering of objects based on pattern types.
Example 5.2.1:
(setf note (item (items 60 84 56 in cycle)))
In example 5.2.1, note is the slot-name. We use item as the item-stream-accessor that accesses one value at a time from the item stream and assigns it to the note slot. We select one of the item-stream-data-types (Table 5.2.1) using one of the item-stream-pattern-types (Table 5.2.2). In the case of Example 5.2.1, the item stream data type is items and the item-stream-pattern-type is cycle. The cycle pattern type circularly selects items reading from left to right for as many events are required.cycle is the default item-stream-pattern-type. Manipulating patterns using item streams is a simple yet very powerful approach to algorithmic composition.
Let's consider another example.
Example 5.2.2: item-streams.lisp
(setf duration rhythm))
After the container is mixed, Common Music returns the following objects:
Example 5.2.3: Output from item-streams.lisp
Notice that the note slot has been assigned one item from the item stream. The assignment is cyclic and continues for the ten events specified by the container's length slot. A rest object is created using the symbol r. The amplitude and rhythm slots are also assigned in cycle by default. The rest is assigned a rhythmic value of .7 seconds because of the cyclic pattern used to assign the rhythm slot. The duration slot is dependent on the value of the rhythm slot. Notice that MIDI channel 0 was assigned in the container initialization list. Slots that do not vary may be assigned in the container initialization list. Example 5.2.2 also demonstrates the use the semi-colon or #| |# to make comments in Common LISP and Common Music.
Table 5.2.1 describes the different item stream data types. All of the item stream data types end in the letter "s." This naming convention helps you distinguish between item stream data types and item-stream-accessors that do not end in the letter "s."
Table 5.2.1: Item Stream Data Types
Item Stream Data Types | Description | Example on accompanying compact disc | Slot Use |
amplitudes | Constructs item streams of symbolic amplitudes in the range fff - pppp | amplitudes.lisp | amplitude slot |
degrees | Constructs item streams of symbolic note names or keynumbers | degrees.lisp |
note slot
Similar to item stream data types pitches and notes
|
intervals | Constructs item streams from a list of integer intervallic distances given a specified starting value | intervals.lisp | note slot |
items | Constructs items streams of floating point and/or integer values | items.lisp | note, amplitude, duration, rhythm, or channel slots |
notes | Constructs item streams of symbolic note names or keynumbers | notes.lisp |
note slot
Similar to item stream data types degrees and pitches
|
numbers | Constructs item streams of cyclic or random numbers using constructor options | numbers.lisp |
Note, amplitude, duration, rhythm, or channel slots.
Implements the following options:in, from, to, below, downto, above, and by
|
pitches | Constructs item streams of symbolic note names or keynumbers | pitches.lisp |
note slot
Similar to item stream data type degrees and notes.
|
rhythms | Constructs item streams of ordinal or symbolic note values | rhythms.lisp |
rhythm or duration slots
May be used with the item stream modifier tempo to set the local tempo of a particular container distinct from the value of *standard-tempo*.
|
steps | Constructs items streams based on intervallic distances. Pitches or keynumbers are calculated in relation to *standard-scale* | steps.lisp | note, channel, or any slot that uses integers |
Table 5.2.2 describes many of the item stream pattern types.
Table 5.2.2: Item Stream Pattern Types
Item Stream Pattern Types | Description | Example on accompanying compact disc | Notes |
accumulation | Plays through the data listed after the item stream constructor iteratively adding one element at each iteration. | accumulation.lisp | May use stream modifier for <integer> to return a specified number of patterns |
cycle | Circles through the data listed after the item stream constructor. | cycle.lisp |
Default pattern type if a pattern type is not specified.
May use stream modifier for <integer> to return a specified number of patterns
|
heap | Plays through the data listed in random but will not replay an event until all others like it have been played. | heap.lisp | Implements with and previous option value pairs (see g.html) |
palindrome | Plays through the data listed after the item stream constructor forwards and backwards. | palindrome.lisp | The pattern type option elided may have a value of T or NIL and determines if events are repeated as the pattern types changes direction. May use stream modifier for <integer> to return a specified number of patterns |
random | Plays through the data listed according to a user-specified random distribution. | random.lisp | May use stream modifier for <integer> to return a specified number of patterns |
rotation | Plays through the items by cycling through the list of items then rotating subsets of the list | rotation.lisp | May use stream modifier for <integer> to return a specified number of patterns |
sequence | Plays through the data listed after the item stream constructor and plays the last element in the list until no more events are required. | sequence.lisp | May use stream modifier for <integer> to return a specified number of patterns |
A number of macros may be used with item streams to implement a specific functionality. Table 5.2.3 describes some of the macros that may be used in conjunction with item streams.
Table 5.2.3: Item Stream Macros
Macro | Description | Example of CD | Notes |
chord | Creates a pitch simultaneity. May optionally enclose pitches in [] to indicate a chord. | chords.lisp | |
crescendo | Creates a gradual increase in amplitude | crescendo.lisp | Requires modifier in to specify number of events in the crescendo |
diminuendo | Creates a gradual decrease in amplitude | diminuendo.lisp | Requires modifier in to specify number of events in the diminuendo |
expr | Creates items based on the value of an expression | expr.lisp | |
mirror | Creates a stream followed by a retrograde of the stream | mirror.lisp | May be used with an item stream pattern type |
repeat | Creates a specified number of repeats of an item stream | repeat.lisp | When used in conjunction with length container initialization.length has higher precedence than repeat in determining number of events. |
retrograde | Creates a retrograde of an item stream. The last note of the initial item stream is repeated before the retrograde. | retrograde.lisp | |
series | Creates an item stream for serial row operations. | series.lisp | Implements options for prime (p), retrograde (r), inversion (I) and retrograde inversion (ri). Also implements options for multiple (returns product of multiple and specified pitch class) and modulus (returns result of modulo operator for specified pitch class). |
5.3 A Complete Example
Example 5.3.1 uses a merge container called my-first-merge saved as my-first-merge.lisp. The merge container contains 6 generators:generator 1, 2, 3a, 3b, 3c, and 3d. The merge is based on the octatonic scale , initially presented in series. 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.
A user-defined function must be evaluated by the interpreter or compiled and loaded into Common Music before the function is used in a Common Music container. It is good practice to include user-defined functions at the beginning of the file that creates containers that reference the functions.
To listen to the merge, load my-first-merge.lisp into Common Music or simply copy the source code and paste it into Stella. Stella creates my-first-merge. If you type the Common Music command list, you will see that not only did Stella create my-first-merge, but the generators have also been created.
Example 5.3.2
Change your focus object to Generator 1a to view its position in the hierarchy.
Example 5.3.3
Stella [Top-Level]: go 2
Return to the first level and mix.
Example 5.3.4
Stella [1a]: up
You should hear the output from my-first-merge with the generators entering as specified in their container initialization lists.
5.4 Suggesting Listening
"U" (The Cormorant) for violin, computer, and quadraphonic sound composed by Mari Kimura is motivated by the blight of the oil-covered cormorants in the Persian Gulf. The formal structure of the composition is quasi-palindromic, imitating the shape of the letter "U." [Kimura, 1992]