spobooks bbv9810.0001.001 in

    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

    (setf note 60)
    (setf amplitude .5)

    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

    (generator my-first-generator midi-note (length 1)
    (setf note 60)
    (setf amplitude .5)
    (setf rhythm .25)
    (setf duration .7)
    (setf channel 0))

    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.

    Example 5.1.3: my-first-generator.cm
    Stella [My-First-Generator]: archive
    Archive objects: (<cr>=My-First-Generator)
    Archive file: (<cr>=home:test.cm) Macintosh HD:Desktop Folder:my-first-generator.cm
    Archiving Macintosh HD:Desktop Folder:my-first-generator.cm.

    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.

    Example 5.1.4
    Stella [My-First-Generator]: open my-first-generator.mid
    Stream: #<Copy-Stream: my-first-generator.mid.copy>
    Stella [My-First-Generator]: mix
    Mix objects: (<cr>=My-First-Generator)
    Start time offset:(<cr>=None)

    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.

    Use the following template to assign slots using items streams:
    (SETFslot-name (item-stream-accessor (item-stream-data-typeitem-stream-data in item-stream-pattern-type)))

    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

    (generator item-streams midi-note (length 10 channel 0)
    ; a semi-colon proceeds a comment
    (setf note (item (items 60 84 r in cycle)))
    #|
    You may also enclose a comment in #| and |#
    |#
    (setf amplitude (item (items .2 .4 .6)))
    (setf rhythm (item (items .3 .5 .7)))

    (setf duration rhythm))

    After the container is mixed, Common Music returns the following objects:

    Example 5.2.3: Output from item-streams.lisp

    Item-Streams:
    1. #<MIDI-NOTE | 60| 0.300| 0.250| 0.200| 0|>
    2. #<MIDI-NOTE | 84| 0.500| 0.250| 0.400| 0|>
    3. #<REST 0.7#x63EA0D6>
    4. #<MIDI-NOTE | 60| 0.300| 0.250| 0.200| 0|>
    5. #<MIDI-NOTE | 84| 0.500| 0.250| 0.400| 0|>
    6. #<REST 0.7#x63EA19E>
    7. #<MIDI-NOTE | 60| 0.300| 0.250| 0.200| 0|>
    8. #<MIDI-NOTE | 84| 0.500| 0.250| 0.400| 0|>
    9. #<REST 0.7#x63EA276>
    10. #<MIDI-NOTE | 60| 0.300| 0.250| 0.200| 0|>

    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.

    Example 5.3.1: my-first-merge.lisp
    #|
    Define a function to scale the duration slot.
    The function scale-duration is used in generator 1a.
    |#
    (defun scale-duration (a-number a-percentage)
    (* a-number a-percentage))
    (merge my-first-merge ()
    #|
    my-first-merge is the container that performs parallel processing of its generators 1, 2, 3a, 3b, 3c and 3d.
    |#
    (generator 1a midi-note (length 48 channel 0)
    #|
    Generator 1 states the fundamental pitch material for the merge, presented melodically in series
    |#
    (setf note (item (series 0 1 3 4 6 7 9 10 from 'ef3
    forming (items p i r ri in random)
    returning note)))
    (setf rhythm (item (items .22 .23 .25 .26 .28 .29 .31 in accumulation)))
    (setf amplitude (item (crescendo from pppp to fff in 48)))
    (setf duration (scale-duration rhythm amplitude)))
    (generator 2a midi-note (start 7 end 11.5 channel 1)
    #|
    Generator 2 provides chords that punctuate the melodic material
    |#
    (setf note (item (notes [c2 d3 ef4 f5] r [cs1 ds2 e3 fs4 gs5] in random)))
    (setf rhythm (item (rhythms e e. (s weight .5) s. in random)))
    (setf amplitude (item (amplitudes mf f ff in heap)))
    (setf duration (/ rhythm 2)))
    (generator 3a midi-note (start 2.8 length 4 channel 2 duration .1)
    #|
    Generators 3a-3d provide an accompaniment to pitch material
    |#
    (setf note (item (notes c4 d5 ef6 f6)))
    (setf amplitude (item (crescendo from mp to mf in 4)))
    (setf rhythm (item (items .01 .035 .048 in heap))))
    (generator 3b midi-note (start 3.75 length 5 channel 0 duration .1)
    (setf note (item (notes cs3 ds4 e5 fs6 gs7)))
    (setf amplitude (item (crescendo from mp to mf in 5)))
    (setf rhythm (item (items .01 .035 .048 in heap))))
    (generator 3c midi-note (start 4.1 length 4 channel 0 duration .1)
    (setf note (item (notes c4 d5 ef6 f6)))
    (setf amplitude (item (crescendo from mp to mf in 4)))
    (setf rhythm (item (items .01 .035 .048 in heap))))
    (generator 3d midi-note (start 4.8 length 7 channel 0 duration .1)
    (setf note (item (notes [cs2 ds3 e4] r [ds3 e4 fs5] [e4 fs5 g6] [fs5 g6 gs7] [g6 a7 as8])))
    (setf amplitude (item (crescendo from ppp to mp in 5)))
    (setf rhythm (item (items .12 .124 .126 .128 .132 in heap)))))
    Notice the user-defined function SCALE-DURATION in Generator 1.SCALE-DURATION is defined as:
    (defun scale-duration (a-number a-percentage)
    (* a-number a-percentage))

    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.

    audio file my-first-merge.mp3

    Example 5.3.2

    Stella [Top-Level]: list
    Top-Level:
    1. #<MERGE: My-First-Merge>
    2. #<GENERATOR: 1a>
    3. #<GENERATOR: 2a>
    4. #<GENERATOR: 3a>
    5. #<GENERATOR: 3b>
    6. #<GENERATOR: 3c>
    7. #<GENERATOR: 3d>

    Change your focus object to Generator 1a to view its position in the hierarchy.

    Example 5.3.3

    Stella [Top-Level]: go 2

    Focus: 1a
    Type: Generator
    Status: Normal
    Position: 1 in My-First-Merge
    Objects: 0
    Start: unset

    Return to the first level and mix.

    Example 5.3.4

    Stella [1a]: up

    Focus: My-First-Merge
    Type: Merge
    Status: Normal
    Objects: 6
    Start: unset
    Stella [My-First-Merge]: mix
    Mix objects: (<cr>=My-First-Merge)
    Start time offset:(<cr>=None)
    ; Warning: MIDI already open.
    ; While executing: MIDI-OPEN

    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]