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 6: The Stella Command Interpreter
In Chapter 5, we discussed some basic commands issued to the Stella command interpreter such as new, go, archive, and open. The Stella command interpreter provides a number of powerful commands that allow you to edit slots after computing a container. Editing specific slots is useful if you like the output from a container, but want to make a few adjustments to the output. To demonstrate these slot editing commands, we create a generator named slot-editing and assign its slots using item streams as shown in Example 6.1.
Example 6.1: slot-editing.lisp
6.1 Referencing Slot Data
Common Music lists up to 50 objects in a container using the list command.
You may reference individual objects by specifying the integer identifier for a particular object.
You may use list to reference a range of objects by specifying an inclusive upper and lower bound of the range separated by a colon.
The list command also references a range of objects with an optional step size. In Example 6.1.4, a step size of two following the lower and upper bound references every other object.
Common Music commands may use the wildcard symbol *. In Example 6.1.5, the wildcard * indicates that all items should be listed.
The wildcard may also be used to specify the upper or lower bound of a range. Example 6.1.6 uses the wildcard in the upper bound position to reference the last object in the container.
The symbol end may also be used to reference the last object in a container.
The symbol end allows relative referencing. Example 6.1.8 demonstrates use of the list command in conjunction with end -1 to specify one less than the last object.
6.2 Assigning Slot Data using set
The set command may be used in conjunction with object referencing to reassign slots. It is easy to confuse set and SETF. Use SETF to assign slots or global variables. Use the set command to assign slots after the slot has been assigned.
In example 6.2.1, we use set to reassign a range of objects. MIDI note objects 2, 3, and 4 are reassigned an amplitude value of .75.
Item streams may also be used with the set command to reassign slots.
Notice that when item streams are used in conjunction with the set command, we do not need an item stream accessor.
Item stream pattern types may also be used with the set command.
More than one slot may be assigned with the same set command as seen in Example 6.2.4. The first midi-note object is assigned a MIDI channel of 1 and the rhythm and duration slots are assigned .75. The duration slot is assigned by first evaluating the rhythm slot.
6.3 Using Other Functions to Reassign Slot Values
Common Music has several commands besides set to assign slot values. Let's apply some of these commands to the objects created in Section 6.2.The retrograde command reverses the order of objects. Optionally, you may specify a range. The retrograde template is:
retrograde <optional range>
Stella [Slot-Editing]: retrograde 1:3
retrograde is not only a command, but also an item stream macro as explained in Chapter 5. Example 6.3.2 demonstrates use of retrograde as an item stream macro to reverse the order of an item stream.
The invert command reverses the direction of each interval from a specified note. Optionally, you may specify a range. The invert template is:
invert < range> <slot> <expression>
The first inverted note slot is calculated by the intervallic distance between the original note slot (d4) and the note reference (ef5). The intervallic distance is a minor ninth. The first inverted note is a minor ninth (or its enharmonic equivalent, the augmented octave) above the note reference. Subsequent notes are inverted in relation to the original note series. For example, if the original note series is an ascending major second, the inverted note series is a descending major second. The wildcard indicates that all midi-note objects should invert the note slot.
The increment command increases or decreases a specified slot by a specified value or expression. The increment template is:
increment <range> <slot> <expression>
In the following example, we subtract .2 seconds from the value of all of the rhythm slots.
The transpose command transposes the note slot a specified interval measures in half steps. The transpose template is:
transpose <range> <slot> <expression>
The following example transposes all notes down one octave.
The shuffle command randomly reorders objects. The shuffle template is:
shuffle <optional range>
The scale command scales objects by a specified percentage. The scale template is:
scale <range> <slot> <percentage>
Example 6.3.7 references the first midi-note and scales its duration by 50%.
6.3 Scale Predicates
Common Music has predicate functions that test the value of the note slot in relation to a reference. These predicate functions all begin with scale and have appended to them a relational operator as shown in Table 6.3.1. The reference may be a keynumber, frequency, or symbolic note name. Symbolic note names must be quoted.
Note that these scale predicates are different from the scale command as described in Example 6.3.7. Scale predicates return a T or NIL value whereas the scale command alters the value of a slot by a specified percentage.
Table 6.3.1: Scale Predicates
Predicate | Example | Explanation |
scale= reference | (scale= 'fs5) | Returns T if scale reference is f-sharp 5 |
scale/= reference | (scale/= 'fs5) | Returns T if scale reference is not f-sharp 5 |
scale< reference | (scale< 'fs5) | Returns T if scale reference is less than f-sharp 5 |
scale> reference | (scale> 'fs5) | Returns T if scale reference is greater than f-sharp 5 |
scale<= reference | (scale<= 'fs5) | Returns T if scale reference is less than or equal to f-sharp 5 |
scale>= reference | (scale>= 'fs5) | Returns T if scale reference is greater than or equal to f-sharp 5 |
Scale predicates may be used in conjunction with the map command as seen in Section 6.4 or with conditionals as discussed in Chapter 9.
6.4 Mapping
The map command is a powerful way to evaluate slot data based on a clause. A clause may be a way to gather information about slots or a condition applied to slots. To apply a clause to a range of slots, specify the objects to be mapped, and then the clause to be applied to those objects. The map command maps the clause to the specified slots. The map template is:
map <objects> <clause>
For <objects>,map uses object referencing as discussed in Section 6.1. The power of the map command lies in the <clause>. One type of clause that map uses is the information clause . Information clauses return information about the mapped objects.
Table 6.4.1 describes map' s information clauses and gives an example of its use. The returned value is based on the following midi-notes in the container named slot-editing:
Table 6.4.1
Information Clause | Description | Example | Evaluation |
---|---|---|---|
collect | Gathers the slot data in a specified range. Returns the number of objects evaluated and the slot data of those objects as a list. | map 1:3 collect channel |
CLAUSE
COUNT VALUE
collect channel
3 (0 2 1)
|
sum | Adds the slot data in a specified range. Returns the number of objects evaluated and the sum of the slot data. | map 1:3 sum rhythm |
CLAUSE
COUNT VALUE
sum rhythm
3 2.4
|
count | Tallies the number of objects for a particular slot. Returns the number of objects evaluated. | map * count note |
CLAUSE
COUNT VALUE
count note
5 5
|
minimize | Finds the smallest numeric value of a specified range. Returns the number of objects evaluated and the smallest value. | map * minimize amplitude |
CLAUSE
COUNT VALUE
minimize amplitude
5 0.35
|
maximize | Finds the largest numeric value of a specified range. Returns the number of objects evaluated and the largest value. | map * maximize amplitude |
CLAUSE
COUNT VALUE
maximize amplitude
5 0.95
|
lowest | Finds the lowest symbolic note name of a specified range. Returns the number of objects evaluated and the lowest value. | map 1:3 lowest note |
CLAUSE
COUNT VALUE
lowest note
3 E5
|
highest | Finds the highest symbolic note name of a specified range. Returns the number of objects evaluated and the highest value. | map 3:5 highest note |
CLAUSE
COUNT VALUE
highest note
3 FS5
|
average | Calculates the average for a specified number of objects for a given slot. Does not work with symbolic note names | map * average duration |
CLAUSE
COUNT VALUE
average duration
5 1.1
|
find | Locates the integer reference for objects that match a condition. | map * find (scale> note 'e5) |
CLAUSE
COUNT VALUE
find (scale> note 'e5) 4 Slot-Editing [2:5]
|
analyze | Performs a statistical analysis on slot for a specified range. Analysis includes number of unique values, minimum, maximum, mean, variance, deviation and a breakdown. | map * analyze rhythm |
CLAUSE
COUNT VALUE
analyze rhythm
5
Unique:3
Minimum: 0.300
Maximum: 1.800
Mean: 0.950
Variance: 0.613
Deviation: 0.783
|
map may also use Common LISP conditionals such as WHEN and UNLESS. First, let's discuss WHEN and UNLESS before resuming our discussion of the map command and conditional clauses.
The Common LISP macros WHEN and UNLESS use the following templates:
(WHEN <CONDITION> <CONSEQUENT-CLAUSE>)
(UNLESS <CONDITION> <CONSEQUENT-CLAUSE>)
With WHEN, if <CONDITION> evaluates to T, LISP evaluates the <CONSEQUENT-CLAUSE>). With UNLESS, if <CONDITION> evaluates to T, LISP does not evaluate the <CONSEQUENT-CLAUSE>).WHEN and UNLESS are logical complements.
Examples 6.4.2 and 6.4.3 reference the value of *standard-tempo* which has a value of 60.
In Example 6.4.2, WHEN is used with the Common LISP condition (= *STANDARD-TEMPO* 60). Since the value of *standard-tempo* is 60, the condition evaluates to T and LISP evaluates the <CONSEQUENT-CLAUSE>. The <CONSEQUENT-CLAUSE> is the quoted symbol 'hello. In Common LISP, quoted objects evaluate to themselves so LISP returns HELLO.
In Example 6.4.3, we test again for the value of *standard-tempo*. This time, we use UNLESS.UNLESS evaluates the <CONSEQUENT-CLAUSE> if the <CONDITION> is NIL. Since the <CONDITION> evaluates to T, LISP does not evaluate the <CONSEQUENT-CLAUSE> and returns NIL.
Now that we understand Common LISP's WHEN and UNLESS, let's resume our discussion of the map command and conditional clauses.
Recall our slot data from Example 6.4.1.
Example 6.4.4 uses the map command in conjunction with WHEN. The map command tells Common Music to evaluate midi-notes 1 through 3. If the amplitude of those midi-notes is greater than .5, map assigns the note slots the symbolic note name c4. Because the amplitude of the first and second midi-notes is greater than .5, the note slots of these objects are assigned c4.
Example 6.4.5 uses the map command in conjunction with UNLESS. The map command tells Common Music to evaluate all of the midi-notes in the current focus object named Slot-Editing through use of the wildcard *. The condition is (= CHANNEL 2). The condition evaluates to NIL for objects 1, 3 and 5 so the note slots of objects 1,3, and 5 are transposed down an octave.
Example 6.4.6 uses the map command with the scale predicate scale=. Midi-note objects 1 through 3 are evaluated to see if their symbolic note name is equal to c4. The scale= predicate evaluates to T for midi-note objects 1 and 2 and their channel is reassigned a value of 3.
6.5 Duplicating A Container
Common Music commands such as set, retrograde, invert, increment, transpose and shuffle are destructive operations. Once these commands are issued, there is no way to "un do" what has been done. For this reason, it is a good idea to make a copy of a container and its contents before issuing these commands.
To copy a container and its contents, use the Common Music command duplicate.
Example 6.5.12. #<THREAD: Slot-Editing-Copy>
duplicate copies a container and its contents to a new object. The new object is automatically placed in Top-Level. In Example 6.5.1, the duplicate command created a new generator named Slot-Editing-Copy placed it in Top-Level.