Professional Documents
Culture Documents
Title Page
Title // Algorithmic composition: a gentle introduction to music composition using common
LISP and common music
Publisher // The Scholarly Publishing Office, The University of Michigan, University Library
Copyright C 2003 by the Regents of the University of Michigan. All rights reserved. No part of
this book may be reproduced without permission of the authors. Authors retain the copyright to
their individual works. The Regents of the University of Michigan David A. Brandon, Ann Arbor;
Laurence B. Deitch, Bingham Farms; Olivia P. Maynard, Goodrich; Rebecca McGowan, Ann
Arbor; Andrea Fischer Newman, Ann Arbor; Andrew C. Richner, Grosse Pointe Park S. Martin
Taylor, Gross Pointe Farms; Katherine E. White, Ann Arbor; Mary Sue Coleman, ex officio
Please contact The Scholarly Publishing Office at the University of Michigan, University Library
for permission information: Scholarly Publishing Office 300 Hatcher N Ann Arbor, MI 48109
lib.spo@umich.edu
The University of Michigan University Library through its Scholarly Publishing Office is
committed to providing academic publishing services that are responsive to the needs of both
producers and users, that foster a sustainable economic model for academic publishing, and that
support institutional control of intellectual assets. The Scholarly Publishing Office seeks to
disseminate high-quality, cost-effective scholarly content through both print and electronic
p u b l i s h i n g . Information about the Scholarly Publishing Office can be found at
http://spo.umdl.umich.edu [http://spo.umdl.umich.edu/] .
Acknowledgements
This book would not have been possible if it were not been for the dedicated work of Heinrich
(Rick) Taube. Rick has worked tirelessly, with the support of Tobias Kunze, on the development
of Common Music. Whenever there was an issue regarding some aspect of Common Music, Rick
or Tobias would quickly tackle the technical problems to make the software more responsive.
I also acknowledge the work of David S. Touretzky for his influential book, "Common LISP: A
Gentle Introduction to Symbolic Computation." Although this book is no longer in print, it has
been an invaluable teaching tool and reference for myself and my students. Touretzky's
emphasis on simplicity has opened the door of algorithmic composition to many music
composition students.
I would also like to thank my students who carefully reviewed this book prior to publication:
Todd Bauer, Nathaniel Cartier, Michael Chiaburu, Hiroko Fukudo, Matthew Gill, Colin Meek,
Christopher Peck, Nathan Proulx, Jennifer Remington, Christopher Rozell, Stephen Alex
Ruthmann, Michael Vartanian, and John Woodruff. Their thoughtful comments and
contributions have increased the accessibility and relevance of the content.
Preface
This book is about learning to compose music using the programming language Common LISP
and the compositional environment Common Music developed by Heinrich Taube.
The motivation for writing this book comes from several years of teaching music and
engineering students the fundamentals of algorithmic composition. Algorithmic composition,
for the purposes of this book, is defined as the use of computers to implement procedures that
result in the generation of music. The idea of applying algorithms during the composition of
music is pervasive throughout music history. The intent of this book is to give the reader the
fundamentals of Common LISP and Common Music accompanied by examples of
algorithmically based compositions. Although not every aspect of Common LISP and Common
Music are covered in this book, the reader will be well equipped to develop their own algorithms
for composition.
As I wrote this book, I kept the needs of several kinds of readers foremost in mind:
Musicians - These readers know the fundamentals of tonal music theory and have had formal
instruction in music performance and composition. These readers may or may not have studied
a programming language.
Researchers - These readers have experience in both music and engineering and are interested
in quickly and efficiently learning the fundamentals of Common Music.
This book is organized into three sections. Section I, comprised of Chapters 1 through 4,
introduces the fundamentals of programming in Common LISP and Common Music. Section I
also includes a historical survey of algorithmic composition. Section II, comprised of Chapters 5
through 14, is the core content of the book. These chapters contain detailed information on the
integration of Common LISP and Common Music with an emphasis on music composition.
Some readers may choose to reverse their study of recursion (Chapters 13) and iteration
(Chapter 14). Readers with less programming experience generally find it easier to understand
recursion if they have first studied iteration. Chapters 13 and 14 have many parallel examples
designed to help the reader understand the differences between iteration and recursion through
direct comparison. Section III, comprised of Chapters 15 and 16, may be regarded as advanced
topics in algorithmic composition.
The reader may need the assistance of an instructor or Common LISP documentation for
implementation-specific information. Common LISP implementations have their own
commands or user interface. The reader is well advised to make sure their implementation of
Common LISP includes an editor.
I am very much in gratitude to the University of Michigan School of Music for their excellent
music technology facilities, technical staff, and loyal support of my work.
Chapter 1: Introduction
This book integrates three subject areas: Common LISP, Common Music, and algorithmic
composition. The purpose of combining these three subjects into one book is to assist readers
who are interested in composing music using computers. The author assumes the reader has
very little experience with Common LISP or Common Music. For this reason, usage and syntax
of Common LISP and Common Music are explained through numerous carefully documented
examples. The author assumes the reader has an understanding of tonal music theory and MIDI
(The Musical Instrument Digital Interface) [IMA, 1983]. The majority of the examples in this
book and the accompanying compact disc use MIDI.
Many wonderful compositions have been written over the years using Common LISP and
Common Music. By making the details of how to use Common Music more approachable, the
author hopes more people will be enticed to explore the potential of algorithmic composition.
Figure 1.1.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000001.jpg]
Once musical events are described as elements of a list, Common LISP functions may be applied
to each element of the list to transform the elements. One such example might be to transpose
every element of the list in Figure 1.1.1 up a major second returning the list (D F-sharp E G).
Beginning with Chapter 3, we will learn how to use Common LISP to build programs that
transform musical data.
Common Music is implemented in Common LISP and the Common LISP Object System [Keene,
1989]. Common Music's command line interface is called Stella. Common Music runs on
Macintosh, Windows, and UNIX platforms. The software and accompanying electronic
documentation is available as freeware at a number of internet sites [Taube, 2000]. Common
Music requires that Common LISP be installed on your computer system. You may think of
Figure 1.2.1 when conceptualizing the software layers required by Common Music.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000002.jpg]
Common Music works in conjunction with a number of other protocols and applications for the
display and realization of music. For example, it is possible to use MIDI for both input and
output to Common Music. MIDI input and output requires that Common Music communicates
with your system's MIDI driver. Appendix A describes how to configure Common Music for
MIDI input and output using the OMS (Open Music System) MIDI driver. The majority of
examples in this book and accompanying compact disc use MIDI.
Another way to use Common Music is in conjunction with Csound, sound synthesis software
developed by Vercoe and Karstens. Common Music outputs parameter values to a Csound
scorefile that is referenced by a Csound orchestra file. Appendix B describes how to configure
Common Music to output data using the Csound scorefile format [Boulanger, 1999].
Common Music outputs data to a number of other sound synthesis applications including
Common Lisp Music [Schottstaedt, 1991], Cmix [Lansky, 1990], and Cmusic [Moore, 1990] as
well as music notation software such as Common Music Notation developed by Schottstaedt.
Gareth Loy describes criteria for determining the validity of an algorithm [Loy, 1989]. An
algorithm must have a finite number of steps; have both input to and output from the algorithm;
yield a result in a finite period of time; and have a precise definition for each step of the
algorithm. Donald Knuth explains that there are also aesthetic criteria for the evaluation an
algorithm [Knuth, 1973]. These aesthetic criteria include simplicity, parsimony, elegance, and
tractability. Ideally, algorithms should strive to meet the criteria outlined by Loy and Knuth.
Composition is the process of creating a musical work [Apel, 1979]. The term composition
literally means to "put together" parts into a unified whole. The process of composing music is
oftentimes characterized by trial and error. The composer tries something, listens, and
determines if revisions are necessary. The composer is continually evaluating the effectiveness of
a part in relation to the whole.
We combine the terms algorithm and composition to derive the term algorithmic composition.
Algorithmic composition, in the simplest sense, is when a composer uses an algorithm to put
together a piece of music. Since the mid-twentieth century, the computer has become a key
partner in implementing algorithms that generate music. Because of the increased role of the
computer in the compositional process, algorithmic composition has come to mean the use of
computers to implement compositional procedures that result in the generation of music.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000003.jpg]
Let's say that we decide to roll the die six times. Our six tosses return the numbers 2, 5, 3, 9, 3,
and 12. The music that results from our roll of the die is found in Figure 2.1.2.
Figure 2.1.2
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000004.jpg]
Can such a simple algorithm produce interesting music? How do we determine the other aspects
of the composition such as rhythm, timbre, loudness, register, etc.? These questions have been
explored by composers throughout music history.
Over a thousand years later, the work of music theorists such as Guido d'Arezzo established the
framework for our conventional system of music notation. His system employed a staff
accompanied by a clef making it possible for a composer to notate a score so that it could be
performed by someone other than the composer. Prior to the development of the score, music
was learned by rote and generally improvised and embellished by the performing musician. By
the thirteenth century, formalized music composition began to replace improvisation and the
role of composer and performer became increasingly distinct.
The music theorist Franco of Cologne established rules for the time values of single notes,
ligatures, and rests in his treatise Arts canus mensurabilis (ca. 1250). By the early fourteenth
century, composers began to treat rhythm independently of pitch and text. French composers of
the ars nova , such as Phillipe de Vitry and Guillaume de Machaut, used isorhythm as a means
of unifying their compositions. Iso means "same" so isorhythm means literally "same rhythm."
Isorhythm is the practice of mapping a rhythmic sequence, named the talea , onto a pitch
sequence called the color . Figure 2.2.1 depicts the talea from the motet De bon espoir-Puisque
la douce-Speravi by Guillaume de Machaut.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000005.jpg]
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000006.jpg]
The tenor of De bon espoir-Puisque la douce-Speravi was derived by mapping the color onto the
talea as shown in Figure 2.2.3.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000007.jpg]
Phillipe de Vitry used a palindrome in the construction the talea for his isorhythmic motet
Garrit gallus - In nova fert. A palindrome is a pattern that reads the same forwards as it does
backwards. Figure 2.2.4 shows the organization of the palindrome by measure. Measure 1
compares to measure 9, measure 2 compares to measure 8, etc.
Figure 2.2.4: The talea of Garrit gallus - In nova fert by Phillipe de Vitry
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000008.jpg]
The Renaissance period witnessed the rise of polyphonic sacred and secular musical forms. By
the Baroque Period (1600-1750), highly developed contrapuntal forms such as the canon and
fugue flourished. One of the great masters of contrapuntal forms was Johann Sebastian Bach.
During the final years of his life, J.S. Bach composed such didactic contrapuntal works such as
the Musical Offering and The Art of the Fugue [Bach, 1752]. The Art of the Fugue is a brilliant
pedagogical tool for the study of counterpoint that systematically documents the procedure of
fugal and canonic composition.
The canon is a highly procedural contrapuntal form. The composer begins with a melody, called
the leader, which is strictly followed at a delayed time interval by another voice, called the
follower. Sometimes, the follower may present a variation of the leader through transposition,
augmentation, or inversion. Figure 2.2.5 shows an excerpt from The Art of the Fugue by J.S.
Bach that is a canon in both augmentation and inversion.
Figure 2.2.5: Canone I from The Art of the Fugue by J.S. Bach
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000009.jpg]
In the final fugue of The Art of the Fugue, Fuga XV , J.S. Bach uses his own name,B-A-C-H, as
the subject of the fugue: B-flat, A, C, and H. (H is the German letter for B). His name is
embedded in one of the most masterful contrapuntal works of all time.
Figure 2.2.6: Excerpt from Fuga XV from The Art of the Fugue by J.S. Bach
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000010.jpg]
One of the most often cited examples of algorithmic music in the Classical Period (1750-1827) is
Musikalisches Würfelspiel by Wolfgang Amadeus Mozart (1756-1791). In this composition,
Mozart composed discrete musical excerpts that could be combined to form a waltz. The order of
musical excerpts was determined by rolling two six-sided dice. The person assembling the waltz
would refer to a table created by Mozart that showed which music should be used for the values
of 2-12 on the dice.
Romanticism pushed the harmonic vocabulary into the extreme use of chromaticism. After
Richard Wagner (1813-1883), there was very little a composer could do that would be considered
novel using tonal music theory. Arnold Schoenberg, and his pupils Anton Webern and Alban
Berg, established new procedures for composition called serial composition .
In serial composition, the composer works with a series of twelve chromatic tones of equal
importance. In strict serial composition, no tone may be repeated until all twelve have been
used. The total number of twelve-note series is 479,001,600 [Brindle, 1969] which greatly
expands the melodic and harmonic vocabulary of the late Romantic period. Because of the equal
importance of the twelve chromatic tones, serial composition eroded tonality and gave rise to
atonality.
Algorithmic procedures lend themselves well to serial composition. To introduce variation into a
serial composition, the composer may use permutations of the tone row derived from
transposition, inversion, retrograde, or retrograde inversion of the row. Figure 2.2.7 shows the
tone row used by Alban Berg in the Lyric Suite for string quartet composed in 1926.
Figure 2.2.7: The tone row for the Lyric Suite by Alban Berg
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000011.jpg]
Figure 2.2.8 shows a matrix that was constructed based on the tone row from Alban Berg's Lyric
Suite . The Rows are numbered 1-12 and the Columns are labeled A-L. The original form of the
tone row is found in Row 1, Columns A-L, reading from left to right. The retrograde form of the
tone row is found by reading Row 1, Columns A-L, reading from right to left. The inversion of
the tone row is found in Column A reading Row 1 to Row 12 and the retrograde inversion is
found in Column A reading from Row 12 to Row 1. Each Row and Column is further labeled with
T followed by a value in the range 0-11. The T stands for transposition and the number is the
level of transposition measured in half steps from the original tone row. For example, T5 means
the tone row has been transposed up five half steps from the original form (e.g. a Perfect
Fourth).
Figure 2.2.8: Tone Row Matrix for Alban Berg's Lyric Suite
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000012.jpg]
About the same time Alban Berg completed the Lyric Suite , Iannis Xenakis (1922-) began to
make his way in the world. Xenakis received an engineering degree from the Athens Polytechnic
School and studied music composition with Honegger, Milhaud, and Messiaen and architecture
with Le Corbusier. Xenakis was keenly interested in the application of mathematics to music
composition. In 1966, Xenakis founded the School of Mathematical and Automated Music in
Paris. His music is described as stochastic music meaning he uses probability theory in the
selection of musical parameters. Xenakis exploited probability theory in his search for new
musical form and structure. One of Xenakis' well-known works, Pithoprakta (1955-56), creates
dense sound masses determined by probabilistic methods.
The music of Karlheinz Stockhausen (1928-) stands in sharp contrast to that of Iannis Xenakis.
Stockhausen developed serial composition to its extreme by not only applying serial methods to
pitch, but also rhythm, dynamics, timbre, and density. Stockhausen was influenced by the
German philosopher Hegel and his doctrine on the unity of opposites. Stockhausen applied
Hegelian philosophy by using calculations to pre-compose his music while integrating chance
operations into the performance. A stunning example of his work is the Klavierstück X1 (1956)
composed for piano. The score, measuring about thirty-seven inches by twenty-one inches,
consists of nineteen carefully composed segments that the pianist performs in whatever order
his or her eye happens to fall upon the score. Stockhausen employed chance operations similar
to those explored by Mozart in his Musikalisches Würfelspiel almost two hundred years earlier.
About the same time Stockhausen composed the Klavierstück X1, Lejaren Hiller and Leonard
Issaacson were preparing to significantly alter the course of music history. Ada Augusta,
Countess of Lovelace worked with Charles Babbage on the development of a mechanical
computer called the Analytical Engine. In 1842, she described the use of the computer in the
creation of music and foretold the era of computer-assisted composition heralded by Hiller and
Isaacson:
"The operating mechanism [of the Analytical Engine]. . .might act upon other things. . . whose
mutual fundamental relations could be expressed by those of the abstract science of operations,
and which should be also susceptible of adaptations to the act ion of the operating notation and
mechanism of the Engine. Supposing, for instance, that the fundamental relations of pitched
sounds in the science of harmony and of musical composition were susceptible of such
expression and adaptations, the Engine might compose and elaborate scientific pieces of music
of any degree of complexity or extent." [Roads, 1996]
It was 1957 when Lejaren Hiller and Leonard Isaacson programmed the ILLIAC computer at the
University of Illinois to algorithmically generate music. The output of their software created The
ILLIAC Suite scored for string quartet. The work of Hiller and Issaacson is documented in the
book "Experimental Music." [Hiller, 1959]. By 1962, Xenakis began to use the computer to assist
in the calculations for his compositions Amorsima-Morsima and Strategie, Jeu pour deux
orchestres.
John Cage (1912-1992) was a self-declared indeterminist. Cage integrated Eastern philosophies,
especially Zen Buddhism, and the I Ching Book of Changes, into his compositions. A landmark
collaboration between Cage and Hiller resulted in the multi-media composition HPSCHD (1967-
1969), The composition uses computer print outs, excerpts of traditional music, and visual
elements depicting space and rocket technology. The traditional music is derived from Mozart's
Musikalisches Würfelspiel and his piano Sonatas. Cage's statement, "it is the machine that will
help us to know whether we understand our own thinking processes," demonstrates his
philosophical comradeship with Lejaren Hiller. HPSCHD requires up to seven harpsichords and
fifty-one electronic tapes that are combined in any possible way to achieve unique performances.
T h e composition received its world premiere before an audience of nine thousand at the
University of Illinois on May 16, 1969. The performance included all seven harpsichords, fifty-
one computer-generated tapes, eighty slide projectors, and seven film projectors.
For over two thousand years, composers have used algorithms to assist in the creation of new
works. Algorithms for music composition have evolved into several categories: aleatoric (or
chance) methods [e.g. Cage]; determinacy [e.g. Schoenberg, Webern, and Berg]; and stochastic
(or probabilistic) methods [e.g. Xenakis and Hiller]. Composers are applying not only
mathematical models but also biological paradigms to the creation of music. Since almost any
process may be modeled using a computer, almost any model may be used for music
composition.
The principle questions facing composers who use algorithmic processes are rooted in aesthetics
and philosophy. Why use algorithms in the composition of music? What is more important- the
algorithm or the composition? How does a composer or listener decide if an algorithmic
composition is successful?
To answer this question, one must separate the process of composition from the product of
composition, e.g. the music. Some algorithmic composers would argue that the aesthetic merit of
an algorithmic composition should be based solely on the algorithm used to create the music.
Other composers assert that both the algorithms used to create the composition and the
composition itself should be assessed when determining aesthetic merit. There are still others
who would argue that the algorithms used in algorithmic composition are simply a means to an
end and for that reason, the algorithms themselves are not worthy of artistic scrutiny. These
composers may believe that their success or failure as a composer is based on the listener's
response, and therefore they have the right to throw away or modify the output of an algorithm
to achieve their aesthetic goal.
All of these responses to the process and product of algorithmic composition are valid as each
view is simply a manifestation of a personal aesthetic. Unfortunately, composers of algorithmic
music have not been formally surveyed regarding their views on the aesthetics of algorithmic
composition so we do not know how many composers fall into which category at any given time
or if there are more categories to consider.
In the absence of a formal survey, we let the repertoire of algorithmic composition speak for
itself. In reviewing algorithmic processes throughout the twentieth century, the number of
compositions that are supported by documented algorithms are dwarfed by those that are not. In
fact, when asking composers to provide algorithms accompanied by software implementation for
this book, many composers confided that their code is not up to Knuth's standards of simplicity,
elegance, parsimony, and tractability. With rapid advances in automated music composition
systems, and the tendency to embed the process in a technology, it is inherently more difficult
for the process to survive than it is the product.
A group of visual and sonic artists developed their own criteria for determining the aesthetic
merit of electronic art, including computer music [Mandelbrojt, 1999]. These artists felt the
criteria should be based on the poetic quality of the artist's vision; a successful relationship
between the artist's idea and its realization, especially when the idea cannot be materialized
through simpler traditional means; the efficiency with which the artist's idea is conveyed; and
the originality of the idea or its realization. The evaluation of each of these criteria is not simply
a "yes" or "no" as in the case of Knuth's criteria. On the contrary, the criteria summarized by
Mandelbrojt require a graded continuum for each criterion to render an aesthetic judgement
about a work. These criteria also presuppose that the person performing the evaluation knows
something of the artist's intent. Unfortunately, such is not always the case.
Algorithmic composers need aesthetic criteria that are neither as limiting as Knuth's nor as
assumptive as those described by Mandelbrojt. Composers of algorithmic music are obliged to
carefully examine the quality of their artistic idea and the efficiency in which that idea is
presented. The composer must create a successful relationship between the artistic idea and the
technological medium in which they work. The work must demonstrate originality or significant
refinement of an existing idea and maintain the highest quality of technical production.
Numbers may take several forms including integers or whole numbers, ratios or fractions, and
floating point numbers.
Example 3.1.1
Integers: -3, 0, 23, 8987
Ratios: 23/8, 1/2
Floating Point Numbers: -3.222, 3.1459,
0.0
Another data type in Common LISP is a symbol. Symbols look like words and may contain any
combination of letters and numbers along with some special characters like the hyphen (-) or
underscore (_).
Example 3.1.2
Symbols: scale, f7-chord, b-flat_major
There are two special symbols in Common LISP: T and NIL. T represents TRUE or YES and NIL
represents FALSE, NO, or NOTHING.
Another data type in Common LISP is a string. A string is a sequence of characters enclosed in
double quotes.
Example 3.1.3
Strings: "A string is anything enclosed in double quotes!"
"Is this a string?"
A very important Common LISP data type is the list. Lists are at the heart of programming in
LISP, which derived its name from LISt Processing. A list is a collection of one or more things
enclosed in parentheses. If the list includes more than one thing, they are space delimited. Each
thing in the list is called an element. Therefore the list (C-MAJOR D-MAJOR F-MAJOR) has
three elements and the list(+ 4.5 23/8 .0007 -23) has 5 elements.
Lists may be represented in the computer's memory as a chain of cons cells. You may think of a
cons cell as having two parts. The left part is referred to as the CAR and the right part is referred
to as the CDR. Each part of a cons cell has a pointer. The CAR pointer points to an element in a
list and the CDR points to the next element in the list. Figure 3.1.1 is a cons cell representation of
the list (C-MAJOR D-MAJOR F-MAJOR). Notice that the cons cell chain ends in NIL.
Figure 3.1.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000013.jpg]
Lists that consist of one level of a cons cell chain are called a flat list .
When a list contains another list, it is referred to as a nested list . An example of a nested list is
(C-MAJOR (C E G)). The top-level of the chain of cons cells contains two elements: the
symbol C-MAJOR and the list (C E G). The next level down of the chain of cons cells contains
three elements namely the symbols C, E, and G. Figure 3.1.2 is a cons cell representation of the
nested list (C-MAJOR (C E G)).
Figure 3.1.2
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000014.jpg]
Lists that have the same number of left parentheses and right parenthesis are called well-formed
lists . Both (C-MAJOR D-MAJOR F-MAJOR) and (C-MAJOR (C E G)) are well-formed lists.
Now that we are familiar with some Common LISP data types, it's important to learn how these
data types relate to each other. Both numbers and symbols are categorized as atoms. An atom is
the smallest object that cannot be represented by a cons cell. A list, on the other hand, is a chain
of cons cells. Atoms and lists form what are called symbolic expressions. Figure 3.1.3 gives an
overview of some Common LISP data types.
Figure 3.1.3
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000015.jpg]
Each particular instance of a data type is called an object. Therefore 78.3, MY-CHORD, and (C-
MAJOR (C E G)) are all objects.
A primitive is a built-in function or simply put, a function that LISP already knows about. LISP
has many primitives that operate on numbers, symbols, and lists.
LISP primitives that function on numbers include basic arithmetic operations such as addition,
subtraction, multiplication, and division as well as more advanced arithmetic operations such as
square root.
The syntax for applying data to a function is to enter the function as the first element of a list
followed by the input that is passed to that function. The template for calling a function is:
(FUNCTION INPUT)
The best way to learn how Common LISP works is to use it. Follow along by typing these
examples into your LISP interpreter.
Example 3.2.1
The reader enters the bold text into the interpreter. The information following the bold text is
the evaluation returned by the interpreter.
Common Lisp appears in the COURIER font in all capital letters. Common Music appears in the
helvetica font in all lower-case letters.
? (+ 3 5.6)
8.6
? (- 7 6 5)
-4
Notice how the subtraction primitive accepts more than two inputs. Some Common LISP
functions accept a variable number of inputs. In this case, 6 is subtracted from 7 returning a
value of 1. Next, 5 is subtracted from 1 returning a value of -4.
? (/ 23 8)
23/8
? (/ 23 8.0)
2.875
? (sqrt 25)
5
? (sqrt -25)
#c(0 5)
The square root of -25 returns a complex number with a real part of 0 and an
imaginary part of 5
Functions may be nested. LISP evaluates nested functions by evaluating the innermost set of
parentheses first and working toward the outer-most set of parentheses.
Example 3.2.2
? (+ 3 (- 3 2))
4
? (* 8 (/ 15 3))
40
? (sqrt (/ 23 8))
1.695582495781317
Example 3.2.3
? (float 23/8)
2.875
? (float 4)
4.0
The primitive ROUND returns the integer nearest to its input. If the input is halfway between two
integers (for example 3.5), ROUND converts its input to the nearest integer divisible by 2.ROUND
also returns the remainder of the rounding operation.
Example 3.2.4
? (round 3.5)
4
-0.5
? (round 2.5)
2
0.5
? (round 2.875)
3
-0.125
Example 3.2.5
? (abs -7.2)
7.2
? (abs 14.5)
14.5
LISP has many primitives that function on lists. A useful function is LENGTH, which returns the
number of elements found on the top-level of a list.
Example 3.2.6
? (length '(/ 16 2))
3
Notice that the list passed to LENGTH is preceded by a single quote. The single quote tells LISP
not to evaluate the first element in the list as a function. The single quote or the LISP primitive
QUOTE is helpful when you want to tell LISP not to evaluate something as a function or a
variable. A variable is a place in memory where data is stored. Common LISP variables are
named by symbols.
Try entering some of the quoted elements in the list a combination of upper case and lower-case
letters. You will notice that LISP is not case sensitive for quoted lists.
LISP may have a list of no elements. A list of no elements is referred to as the empty list or NIL
and is represented as () or the symbol NIL. Therefore NIL is both a symbol and a list.
Example 3.2.7
? (length nil)
0
? (length ())
0
You may point selectively to different elements in a list. The primitive CAR or FIRST returns the
first element of a list by pointing to the first element of the list.
Example 3.2.8
? (car '(c-major (c e g) d-major (d f-sharp a)))
C-MAJOR
The primitive CDR or REST returns all but the first element of a list as a list.
Example 3.2.9
? (cdr '(c-major (c e g) d-major (d f-sharp a)))
((C E G) D-MAJOR (D F-SHARP A))
From this, you may observe that FIRST and REST are complementary functions.
Example 3.2.10
? (first nil)
NIL
? (rest ())
NIL
Other LISP primitives selectively point to an element in a list such as second , THIRD, and so
on.
Example 3.2.11
? (second '(c-major (c e g) d-major (d f-sharp a)))
(C E G)
Example 3.2.12
? (last '(c e g))
(G)
The primitive BUTLAST returns the last N elements of a list. The first argument to BUTLAST is
the list and the optional second argument is the number of elements to be omitted. If no second
argument is given, BUTLAST assumes that only one element should be omitted.
Example 3.2.13
? (butlast '(c e g b-flat) 3)
(C)
The primitive REVERSE reverses the elements in a list such that what was first is now last and
vice-versa.REVERSE operates on the top-level of the list.
Example 3.2.14
? (reverse '(c e g b-flat))
(B-FLAT G E C)
The CONS primitive may be used to construct lists. The CONS function creates a cons cell. The
CONS function requires two inputs and returns a pointer to a new cons cell whose CAR points to
the first input and whose CDR points to the second.
Example 3.2.15
? (cons 'a '(d e-flat))
(A D E-FLAT)
Notice that the symbol A as well as the list (D E-FLAT) is quoted. The quote tells LISP not to
evaluate the symbol A nor the list (D E-FLAT).
Example 3.2.16
? (cons 'c-flat nil)
(C-FLAT)
? (cons '(c e g) nil)
((C E G))
LIST creates lists from symbols and/or lists by simply adding a level of parentheses to its
inputs.
Example 3.2.17
The primitive APPEND accepts two inputs. When APPEND is given two lists as its inputs, it
returns a list that contains all of the elements of both lists as a list.
Example 3.2.18
? (append '(c e g) '(b-flat d))
(C E G B-FLAT D)
When APPEND is given a list followed by a symbol, it returns a dotted list. A dotted list is a chain
of cons cell whose last CDR does not point to NIL.
Example 3.2.19
The cons cell representation of the list (C E G . B-FLAT) looks like Figure 3.2.1.
Figure 3.2.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000016.jpg]
LISP evaluates dotted lists differently from flat lists. The evaluation of flat and dotted lists is
best observed by comparing the results of LAST when applied to both flat and dotted lists.
Example 3.2.20
? (last (append '(c e) '(g b-flat)))
(B-FLAT)
In Example 3.2.20, appending two lists (C E) and (G B-FLAT) returns the list (C E G B-
FLAT). The last element of the list is B-FLAT. Appending the list (C E G) with the symbol b-
flat returns the dotted list (C E G . B-FLAT). The CAR of the last cons cell points to G and
its CDR points to B-FLAT.
The primitives CONS, LIST, and APPEND seem very similar. Let's carefully examine how LISP
evaluates these primitives when they are given two lists as input. Figure 3.2.2 show a cons cell
representation of the input to CONS, LIST, and APPEND. Example 3.2.18 shows how LISP
evaluates CONS, LIST, and APPEND given that input. The LISP evaluation is followed by a cons
cell representation of the output.
Figure 3.2.2: Cons cell representation of input to CONS, LIST, and APPEND:
'(C E) '(G B-FLAT)
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000017.jpg]
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000018.jpg]
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000019.jpg]
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000020.jpg]
The primitive RANDOM returns a random number.RANDOM expects a number as its input.RANDOM
returns a random number between 0 (inclusive) and the value of its input (exclusive).
Example 3.2.22
? (random 5)
0
? (random 5)
2
? (random 5)
3
? (random 5.0)
2.7494888990176634
? (random 5.0)
3.2702439432409482
Example 3.3.1
? (symbolp nil)
T
? (symbolp .435)
NIL
Another predicate is NUMBERP. It returns T if the data passed to it is a number and NIL if it is
not.
Example 3.3.2
? (numberp nil)
NIL
? (numberp .435)
T
The predicate FLOATP expects a number as input and returns T if its input is a float and NIL if it
is not.
Example 3.3.3
? (floatp .324)
T
? (floatp 23/8)
NIL
The predicate INTEGERP expects a number as input and returns T if its input is an integer and
NIL if it is not.
Example 3.3.4
? (integerp 23/8)
NIL
? (integerp .324)
NIL
? (integerp -7)
T
Other predicates include ODDP and EVENP. These predicates expect a number as input and
return a T or NIL evaluation if its input is a odd or even number.
Example 3.3.5
? (oddp 5)
T
? (oddp 2)
NIL
? (evenp 5)
NIL
? (evenp 2)
T
The predicate ZEROP expects a number as input and returns a T if its input is 0 and NIL if its
input is not zero.
Example 3.3.6
? (zerop 5)
NIL
? (zerop 0)
T
The predicate PLUSP expects a number as input and returns T if the number is greater than zero
and nil if the number is less than or equal to zero.
Example 3.3.7
? (plusp 3.245)
T
? (plusp 0)
NIL
? (plusp -76)
NIL
The predicate MINUSP expects a number as input and returns T if the number is less than zero.
Example 3.3.8
? (minusp 3.245)
NIL
? (minusp 0)
NIL
? (minusp -76)
T
The relational operators <, >, <=, >=, =, /= (not equal to) compare two or more numbers and
return the result.
Example 3.3.9
? (< 67 34.5)
NIL
? (>= 76 68)
T
? (= 67 67 90 67 67)
NIL
? (/= 4 5)
T
The predicate EQUAL may be used to compare strings or lists.
Example 3.3.10
? (equal "this is a string" "This Is B String")
NIL
The predicate LISTP returns T if its input is a list and NIL if its input is not a list.
Example 3.3.11
? (listp '(c e g))
T
? (listp 4)
NIL
The predicate ENDP expects a list as input.ENDP returns T if its input is the empty list and NIL if
its input is not an empty list.
Example 3.3.12
? (endp nil)
T
Notice that many of the predicates presented thus far have ended in the letter P. There are some
predicates that do not follow this naming convention.
The predicate ATOM returns T if its input is not a cons cell. Otherwise, ATOM returns NIL.
Generally, anything that is not a list is an atom. The one exception is the empty list, which is
both a list and an atom.
Example 3.3.13
? (atom -4)
T
? (atom 'c)
T
? (atom nil)
T
The predicate NULL returns T if its input is the empty list, otherwise NULL returns NIL.
Example 3.3.14
? (null nil)
T
? (null 4)
NIL
? (null ())
T
NULL is similar to ENDP in that both predicates check for the empty list. The primary difference between the two
predicates is that ENDP does not accept numbers as input.
Example 3.3.15
? (endp nil)
T
? (endp ())
T
The logical operators AND and OR may be used to conjoin two or more forms. In LISP, numbers
evaluate to T. Evaluation of AND and OR are based on the truth tables in Figure 3.3.1.
Figure 3.3.1
and
T T T
T F F
F T F
F F F
or
T T T
T F T
F T T
F F F
Example 3.3.16
? (and (numberp 'c-major) (symbolp 'd-major))
NIL
The predicates NOT and NULL return the same results.NULL is generally used to check for the
empty list and NOT is used to reverse a T or NIL evaluation.
Example 3.3.17
? (not (= 4 5))
T
? (not 1)
NIL
? (not 0)
NIL
? (null (= 4 5))
T
? (null 1)
NIL
? (null 0)
NIL
? (null nil)
T
In Example 3.4.1, we define a function named my-c-chord that creates a list of the pitches C, E,
and G.
Example 3.4.1
? (defun my-c-chord ()
(list 'c 'e 'g))
MY-C-CHORD
The function name is MY-C-CHORD. The function does not expect any arguments as input. The
function definition consists of a call to the primitive LIST that constructs a list of the atoms C,
E, and G.
Call the function.
? (my-c-chord)
(C E G)
We call the function MY-C-CHORD with no inputs. The value returned by the function is the list
(C E G).
In Example 3.4.2, we define a function that transposes a given key-number a given interval.
Example 3.4.2
? (defun transpose-midi-note (key-number interval)
(+ key-number interval))
TRANSPOSE-MIDI-NOTE
The function name is TRANSPOSE-MIDI-NOTE. The function expects two inputs, key-number
and interval. The function definition is the sum of the two inputs. The value returned by the
function is the summation of its inputs.
? (transpose-midi-note 60 -5)
55
In Example 3.4.3, we define a predicate function RANGEP that determines if its input is a valid
MIDI keynumber, e.g. an integer in the range 0 - 127.
Example 3.4.3
The function RANGEP expects a number as input. The function definition uses AND to make
certain that all three of the conditions are met before returning a T evaluation. The value
returned by the function is T or NIL.
? (rangep 128)
1 NIL
? (rangep -23.5)
NIL
? (rangep 64)
T
Example 3.5.1:
You may access the documentation string of any function that includes a documentation string
using the primitive DOCUMENTATION. The primitive expects two inputs: a name and if that
name is a symbol or a function. Note that each of the inputs are preceded by a single quote so
that LISP will not evaluate the inputs.
Using DOCUMENTATION, you may learn more about the user-defined function RANGEP.
Example 3.5.2
You may learn more about other Common LISP primitives such as CONSP using
DOCUMENTATION.
Example 3.5.3
? (documentation 'consp 'function)
"returns true if object is a cons, otherwise returns false. consp of the empty list returns false. (See also listp which returns true
on the empty list.)"
Sometimes, you may find yourself wondering if LISP already has a certain function.APROPOS is
a way to query LISP about its built-in functions.APROPOS returns the things that contain the
object that is the argument to APROPOS.
Example 3.5.4: Find out if there is a predicate that tells if its input is a cons cell.
? (apropos 'consp)
CONSP, Def: FUNCTION
The prompt 1> indicates LISP has placed you in the debugger and that you've generated an
error. Consult your Common LISP manual to learn how to your implementation returns you
from the debugger to the interpreter.
To prevent an unbound variable error, make sure your variables are assigned before you use them or quote the
object so that LISP will not evaluate it.
? (atom 'b-flat)
T
? (first 4)
> Error: value 4 is not of the expected type LIST.
> While executing: FIRST
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >
Yet another common error is to pass the wrong number of arguments to a function.
? (transpose-midi-note 60)
> Error: Too few arguments (no opt/rest)
> While executing: TRANSPOSE-MIDI-NOTE
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >
In Chapter 3, we use the term object to refer to an instance of a data type. The number 4, the
symbol f-major and the list '(A B (C D)) are all instances of Common LISP data types. In
CLOS, we extend our definition of an object to be an instance of a data type that may have
certain attributes. In addition, objects may contain methods that are procedures that describe
how the object relates to other objects.
Objects may be grouped into classes . The grouping of objects into classes is dependent on the
relationship among the objects.
Figure 4.1.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000021.jpg]
We can further refine our instruments hierarchy by adding particular objects referred to as
instances of an object. For example, if I play tuba in an ensemble, I could refer to my tuba as the
object my-tuba . my-tuba is a particular instance of the class tuba . my-tuba has all of the
attributes of the class tuba that it inherits from the brass superclass.
The way I perform my-tuba is different from the way someone plays a trumpet . Even though
my-tuba and trumpet inherit from brass , there are differences in the method of
performance.
Figure 4.2.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000022.jpg]
A thread stores musical data such as notes and rests. Data stored in a thread is accessed sequen-tially.
A merge is a container that combines its elements as parallel streams at selected times.
An algorithm computes musical data. The algorithm is re-evaluated each time the algorithm is played.
Subclasses of thread are heap and generator. Subclasses of algorithm are generator and mute. Note in Figure 4.2.2
that generator is both a subclass of thread and algorithm meaning that generators compute slot values and play
sequentially.
Figure 4.2.2
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000023.jpg]
Another class in Common Music is called element. Element has rest and note as its subclasses. A
rest, symbolized by the letter r, increments time but is silent. There are several subclasses of
note depending on the output syntax. For example, the midi-note class has slots that correlate to
the MIDI 1.0 Specification.
Figure 4.2.3
note MIDI keynumber in the range 0-127 None. Slot must be assigned.
amplitude MIDI note-on velocity in the range 0-1.0 Default is .5 or note-on velocity of
(MIDI note-on velocity / 127) 64.
duration The time between a note-on and its note-off Defaults to same as value assigned
to the rhythm slot.
rhythm The time between a note-on and a subsequent None. Slot must be assigned.
note-on
Instances of a container may be positioned in the hierarchy. The highest level of the Common
Music object hierarchy is called Top-Level. By positioning instances of containers in the
hierarchy, the composer may create musical structure on several levels.
To start Stella from Common Music, type (stella) at the ? prompt . If your port of Common Music
has a graphic user interface, from the Common Music menu, select Stella. The prompt changes
from the ? of the interpreter to Stella's Top Level prompt:
Stella [Top-Level]:
Our next step is to create an instance of a container. There are two ways to create an instance of
a container. The first way is to use the command new <container-class> at the Stella prompt
where <container-class> corresponds to the type of container you would like to create.
Example 4.3.1
Stella [Top-Level]: new generator
Name for new Generator: (<cr>=Generator-10) <cr>
New object position: (<cr>=Top-Level)
Example 4.3.2
Stella [Top-Level]:list
Top-Level:
1. #<GENERATOR: Generator-10>
Figure 4.3.1 graphically describes the relationship between the Top-Level and Generator-10.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000024.jpg]
To change our focus object from the Top-Level to Generator-10, use the Common Music
c o m m a n d go <container-name> or go <container-number> where <container-name> or
<container-number> corresponds to the container that you would like to make the focus object.
The focus object is your current position in the hierarchy. Example 4.3.3 references the
container by name. Alternatively, go 1 could have been entered. Referencing a container by
name is called absolute referencing . Referencing a container by its position in the hierarchy is
called relative referencing .
Example 4.3.3
Type: Generator
Status: Normal
Objects: 0
Start: unset
Notice that by going to a specific container in the hierarchy, we are given some information
about that container. In this case, Generator-10 is a container of type generator and is of normal
status (more on this later). Generator-10 contains 0 objects and its start time has been unset.
With the container as our current focus object, we can create instances of new notes to place
inside the container using the Common Music command new midi-note.
Example 4.3.4
Common Music prompts you how many midi-notes you'd like to create. In this case, we choose
1. The default <cr> is used to create as many midi-notes as are required by the algorithm that
makes the slot assignments. The slots are assigned in slot-name - value space-delimited pairs.
Common Music does not care what order you enter the slot-name and value pairs. If the
amplitude, channel, and duration slots are not assigned, they will be assigned a default value as
described in Figure 4.2.3. With Generator-10 as our focus object, we can look down the
hierarchy and see one midi-note.
Figure 4.3.2
Generator-10:
1. #<MIDI-NOTE | 60| 0.500| 0.250| 0.750| 0|>
[missing figure]
Notice that we see a midi-note with slots assignments for note (or keynumber) that has a value
of 60, rhythm that has a value of .5, duration that has a value of .25, amplitude (or velocity) that
has a value of .75, and channel that has a value of 0.
You probably would like to listen to your midi-note. First, make sure you are routing MIDI data
to your MIDI interface. At the Stella prompt, type the Common Music command open midi. If
your port of Common Music has a graphic user interface, you have a menu option to open MIDI.
Example 4.3.4
Example 4.3.5
Stella [Generator-10]: mix
Mix objects: (<cr>=Generator-10)
Start time offset:(<cr>=None)
Common Music assumes you want to mix the container that is the current focus object. Notice
that you can set the start time if you want to delay playback by a specified number of seconds.
Example 4.3.6
Stella [Generator-10]:go
up
Focus: Top-Level
Type: Container
Status: System
Objects: 1
Next, list the containers at Top-Level. Notice the status of Generator-10 has changed from
normal to frozen.
Example 4.3.7
Use the Common Music command go followed by the container name to change the focus object
back to Generator-10.
Example 4.3.8
Once again, we see that Generator-10 has a frozen status. What does it mean when a generator
is frozen? A frozen generator is a generator whose slot values remain fixed until the generator is
unfrozen and re-computed. To unfreeze a generator, use the Common Music command unfreeze
followed by the object name or its integer identifier. Since we only have one note and we did not
describe the slots algorithmically, the frozen state of our generator is of little concern to us.
With Generator-10 as the focus object, create a new thread using the Common Music command
new thread.
Example 4.3.9
Use the Common Music command list to see the new thread in the hierarchy.
Example 4.3.10
Stella [Generator-10]: go 2
Focus: Thread-12
Type: Thread
Status: Normal
Position: 2 in Generator-10
Objects: 0
Start: unset
Example 4.3.12
Stella [Thread-12]: new midi-note
Number of midi-notes to create: (<cr>=*) 1
Slots and values: note 67 rhythm .2 amplitude .6 duration .4 channel
0
New object position: (<cr>=Thread-12)
Example 4.3.13
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000026.jpg]
Now that we know how to add objects to the hierarchy, we should learn how to remove objects
from the hierarchy. Stella employs a two-step process to remove objects. First, items are marked
for deletion using the Common Music command delete (or del ) followed by the object name or
its integer identifier. A deleted item is not played by the mix command. To truly eliminate an
object from the hierarchy, the deleted object must be expunged using the Common Music
command expunge (or exp ). Like the delete command, expunge must be followed by the
object name or its integer identifier.
Example 4.3.14
Stella [Thread-12]:list
Thread-12:
1. #<MIDI-NOTE | 67| 0.200| 0.400| 0.600| 0|>
Example 4.4.1
60.0
You may change the value of a global variable by using the Common LISP macro function SETF.
The template for SETF is:(SETF VARIABLE-NAME VALUE)
You may use SETF to alter the value of *standard-tempo*. For example, to double the value of
*standard-tempo*, type (SETF *STANDARD-TEMPO* (* 2 *STANDARD-TEMPO*)).
Common LISP evaluates the innermost set of parenthesis first and the current value of
*standard-tempo* is multiplied by 2. The product is then re-assigned to *standard-tempo*.
Example 4.4.2
Notice that the SETF is enclosed in parentheses. We use parentheses because SETF is a Common
LISP macro function instead of a Stella command.
If we use relative rhythms and durations such as eighth note, quarter note, half note, etc.,
absolute duration is calculated based on the relative duration and the value of *standard-
tempo*. For example, given a time signature of 4/4 with a tempo of 60 bpm, the absolute
duration of a quarter note is 1/60 of a minute or one second. Figure 4.4.1 shows Common
Music's ordinal and symbolic representation of relative note values.
Figure 4.4.1
sixty-fourth 64th
thirty-second 32nd
sixteenth 16th s
eighth 8th e
quarter 4th q
half 2nd h
whole 1st w
To indicate a dotted note value, modify the corresponding relative note by placing a dot (.)
following the ordinal or symbolic representation. For example, a dotted eighth note would be
represented as e. or 8 th . Additionally, the letter "t " preceding an ordinal or symbolic
representation indicates that the note value is a triplet.
Another Common Music global variable, *standard-scale*, provides the default scale for note
references.
Example 4.4.3
The Common Music global variable *chromatic-scale* represents the equal-tempered chromatic
scale. The chromatic scale is defined over a range of ten octaves. References to individual pitches
are by symbolic note name (A, B, C, D, E, F, G), accidental (N for natural [optional], S for sharp,
or F for flat), and octave designation (00, 0, 1, 2, 3, 4, 5, 6, 7, 8). The Common Music global
variable *standard-scale* has a default value of *chromatic-scale*. In this case, the default value
of a global variable is the value of another global variable.
Example 4.5.2
Stella [Top-Level]: ?
! Show/execute history.
? Show this help.
ADD Add objects to container.
ARCHIVE Archive objects to file.
CHANGE Change the class of objects.
CL Compile/load a file.
CLOSE Close listener if open.
COPY Copy objects to pasteboard.
CUT Cut objects to pasteboard.
DELETE Mark objects as deleted.
DUPLICATE Duplicate container.
EDIT Edit object.
etc.
You may get help on a particular subject by using the Common Music command help followed by
the help topic.
When you type help followed by a help topic, Common Music may be configured to launch a
HTML viewer and open the requested page in the Common Music Dictionary .
The Macintosh port of Common Music offers a graphic interface to on-line documentation.
Example 4.5.3
The Common Music help command opens the Help Window as seen in Figure 4.5.1.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000027.jpg]
You may type in a help topic at the cursor to get more information on that topic. In the case of
Figure 4.5.2, we ask for help on *standard-tempo*.
Common Music is configured to use a HTML viewer that opens the requested page in the
Common Music Dictionary .
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.
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.
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]:
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
Common .lisp File contains Common Music Refer to your Common LISP
Music and Common LISP program documentation on how to create, edit,
LISP code save, compile, and load files.
source
Example 5.2.1:
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.
After the container is mixed, Common Music returns the following objects:
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."
symbolic note
names or
keynumbers
steps Constructs items steps.lisp note, channel, or any slot that uses integers
streams based on
intervallic
distances. Pitches
or keynumbers
are calculated in
relation to
*standard-scale*
accumulation Plays through the data listed after accumulation.lisp May use stream
the item stream constructor modifier for <integer>
iteratively adding one element at to return a specified
each iteration. number of patterns
cycle Circles through the data cycle.lisp Default pattern type if a pattern type is
listed after the item not specified.
May use stream modifier for <integer>
stream constructor. to return a specified number of patterns
heap Plays through the data heap.lisp Implements with and previous
listed in random but will option value pairs (see g.html)
not replay an event until
all others like it have been
played.
palindrome Plays through the data palindrome.lisp The pattern type option elided may
listed after the item have a value of T or NIL and
stream constructor determines if events are repeated as
forwards and backwards. the pattern types changes direction.
May use stream modifier for
<integer> to return a specified
number of patterns
random Plays through the data random.lisp May use stream modifier for
listed according to a user- <integer> to return a specified
specified random number of patterns
distribution.
rotation Plays through the items by rotation.lisp May use stream modifier for
cycling through the list of <integer> to return a specified
items then rotating number of patterns
subsets of the list
sequence Plays through the data sequence.lisp May use stream modifier for
listed after the item <integer> to return a specified
stream constructor and number of patterns
plays the last element in
the list until no more
events are required.
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.
(merge my-first-merge ()
#|
my-first-merge is the container that performs parallel processing of its generators 1, 2, 3a, 3b, 3c and 3d.
|#
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.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/my-first-merge.mp3] my-first-merge.mp3
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
Focus: 1a
Type: Generator
Status: Normal
Position: 1 in My-First-Merge
Objects: 0
Start: unset
Example 5.3.4
Stella [1a]: up
Focus: My-First-Merge
Type: Merge
Status: Normal
Objects: 6
Start: unset
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]
Example 6.1.1
Stella [Slot-Editing]: list
Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.500| 0|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.700| 0|>
You may reference individual objects by specifying the integer identifier for a particular object.
Example 6.1.2
Stella [Slot-Editing]: list 1
Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.500| 0|>
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.
Example 6.1.3
Stella [Slot-Editing]: list 1:4
Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.500| 0|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
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.
Example 6.1.4
Stella [Slot-Editing]: list 1:4:2
Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.500| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
Common Music commands may use the wildcard symbol *. In Example 6.1.5, the wildcard *
indicates that all items should be listed.
Example 6.1.5
Stella [Slot-Editing]: list *
Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.500| 0|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.700| 0|>
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.
Example 6.1.6
Stella [Slot-Editing]: list 2:*
Slot-Editing:
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.700| 0|>
The symbol end may also be used to reference the last object in a container.
Example 6.1.7
Stella [Slot-Editing]: list 2:end
Slot-Editing:
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.700| 0|>
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.
Example 6.1.8
Stella [Slot-Editing]: list 2:end-1
Slot-Editing:
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.600| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.700| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.700| 0|>
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.
Example 6.2.1
Stella [Slot-Editing]: set 2:4 amplitude
.75
Item streams may also be used with the set command to reassign slots.
Example 6.2.2
Stella [Slot-Editing]: set * amplitude (items .35 .65
.95)
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.
Example 6.2.3
Stella [Slot-Editing]: set * channel (items 0 1 2 in
heap)
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.
Example 6.2.4
Stella [Slot-Editing]: set 1 channel 1 rhythm .75 duration
rhythm
Example 6.3.1
Stella [Slot-Editing]: retrograde
Retrograde positions: (<cr>=Slot-Editing)
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.
Example 6.3.2
Stella [Slot-Editing]: set 1:3 channel (retrograde (items 2 1
0))
The invert command reverses the direction of each interval from a specified note. Optionally,
you may specify a range. The invert template is:
Example 6.3.3
Stella [Slot-Editing]: invert * note
ef5
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.
In the following example, we subtract .2 seconds from the value of all of the rhythm slots.
Example 6.3.4
Stella [Slot-Editing]: increment * rhythm
-.2
The transpose command transposes the note slot a specified interval measures in half steps. The
transpose template is:
Example 6.3.5
Stella [Slot-Editing]: transpose * note
-12
The shuffle command randomly reorders objects. The shuffle template is:
Example 6.3.6
Stella [Slot-Editing]: shuffle 1:3
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%.
Example 6.3.7
Stella [Slot-Editing]: scale 1 duration
.5
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.
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 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:
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:
Example 6.4.1
Stella [Slot-Editing]: list
Slot-Editing:
1. #<MIDI-NOTE | E5| 0.300| 0.250| 0.950| 0|>
2. #<MIDI-NOTE | F5| 0.300| 0.500| 0.650| 2|>
3. #<MIDI-NOTE |FS5| 1.800| 2.000| 0.350| 1|>
4. #<MIDI-NOTE | F5| 1.800| 2.000| 0.650| 2|>
5. #<MIDI-NOTE |FS5| 0.550| 0.750| 0.350| 1|>
Table 6.4.1
collect Gathers the slot data in a specified range. Returns map 1:3 CLAUSE
the number of objects evaluated and the slot data of collect COUNT VALUE
those objects as a list. channel collect channel
3 (0 2 1)
sum Adds the slot data in a specified range. Returns the map 1:3 CLAUSE
number of objects evaluated and the sum of the slot sum COUNT VALUE
data. rhythm sum rhythm
3 2.4
count Tallies the number of objects for a particular slot. map * CLAUSE
Returns the number of objects evaluated. count COUNT VALUE
note count note
5 5
minimize Finds the smallest numeric value of a specified map * CLAUSE
range. Returns the number of objects evaluated and minimize COUNT VALUE
the smallest value. amplitude minimize amplitude
5 0.35
maximize Finds the largest numeric value of a specified range. map * CLAUSE
Returns the number of objects evaluated and the maximize COUNT VALUE
largest value. amplitude maximize amplitude
5 0.95
lowest Finds the lowest symbolic note name of a specified map 1:3 CLAUSE
range. Returns the number of objects evaluated and lowest COUNT VALUE
the lowest value. note lowest note
3 E5
highest Finds the highest symbolic note name of a specified map 3:5 CLAUSE
range. Returns the number of objects evaluated and highest COUNT VALUE
the highest value. note highest note
3 FS5
find Locates the integer reference for objects that match map * find CLAUSE
a condition. (scale> COUNT VALUE
note 'e5) find (scale> note 'e5) 4
Slot-Editing [2: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:
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
t h e <CONSEQUENT-CLAUSE>. The <CONSEQUENT-CLAUSE> is the quoted symbol 'hello. In
Common LISP, quoted objects evaluate to themselves so LISP returns HELLO.
Example 6.4.2
Stella [Slot-Editing]: (when (= *standard-tempo* 60) 'hello)
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.
Example 6.4.3
Stella [Slot-Editing]: (unless (= *standard-tempo* 60) 'hello)
NIL
Now that we understand Common LISP's WHEN and UNLESS, let's resume our discussion of the
map command and conditional clauses.
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.4
Stella [Slot-Editing]: map 1:3 when (> amplitude .5) set note '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.5
Stella [Slot-Editing]: map * unless (= channel 2) transpose note -12
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.
Example 6.4.6
Stella [Slot-Editing]: map 1:3 WHEN (scale= note 'c4) set channel 3
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.1
Focus: Top-Level
Type: Container
Status: System
Objects: 2
2. #<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.
7.1 Print
The Common LISP primitive PRINT causes its argument to be printed. The PRINT template is:
(print whatever-needs-to-be-printed)
PRINT will print a constant, symbol, string, or the value of a variable or expression.
The examples in Section 7.1 and 7.2 use the Stella command interpreter. These examples could
also be evaluated by the Common LISP interpreter.
Example 7.1
Stella [Top-Level]: (print 45)
45
45
It seems as if PRINT prints everything twice. One line of output is caused because of PRINT. The
second line of output is printed because LISP returns the last expression evaluated, which in this
case, is the argument to PRINT.
7.2 Format
The Common LISP function FORMAT allows you greater control of the formatting of your output.
The FORMAT template is:
(FORMAT T FORMAT-CONTROL-STRING)
FORMAT is followed by the symbol T to indicate that we want to print to the monitor. A string is
used as the format-control-string.
Notice that FORMAT writes the string without the quotes and returns NIL. The format-control-
string is always enclosed in double quotes. The format-control-string may include FORMAT
directives- special characters in the format-control-string that cause output to appear in certain
ways.FORMAT directives always begin with the tilde (~). Table 7.2.1 gives an overview of some
very useful FORMAT directives.
Table 7.2.1
format Result
Directive
~& Move to a new line only if not already at the start of a new line
~A Print the value of a variable or expression. The value of the variable or expression
is mapped to the format-control-string. The variable or expression is included in
the FORMAT form immediately following the format-control-string.
~n,F Print the value of a variable or expression as a floating point value. The floating
point value is mapped to the format-control-string. n is variable and specifies the
number of positions that will be printed. n is optional.
NIL
In Example 7.2.2, the second ~%FORMAT directive moves printing to a new line. The ~&FORMAT
directive is ignored since printing is already at the start of a new line.
Notice that in Example 7.2.3 the value of *standard-tempo* is mapped to the location of the ~A
FORMAT directive in the format-control-string. Notice that the variable *standard-tempo*
appears after the format-control-string.
Example 7.2.4
Stella [Top-Level]: (format t "~% this is a float ~3,F ~&and this is a float ~4,F" .02 6.98700)
this is a float 0.02
and this is a float 6.99
NIL
In Example 7.2.4, the FORMAT directive ~n,F is used to control formatting of floating point
values.
Returns number n such that lb<=n<ub and n!=exclude. Use exclude to avoid direct repetition.
state is the random state object to use in the calculation, and defaults to *cm-state*.
At first glance, it may not be obvious what between does.between returns a random number
between a lower bound (inclusive) and an upper bound (exclusive). If the upper bound or lower
bound are floating point values, between returns a floating point value. The &optional indicates
an optional argument in Common LISP. In this case, the optional arguments are for exclude and
state.exclude allows you to prevent direct repetition of a randomly-selected value.state is the
random state object used to calculate the random value and defaults to the Common Music
global variable *cm-state*.
We will use PRINT to gain first-hand experience with between. We use between to randomly
specify an amplitude value in the range .5 (inclusive) to .9 (exclusive).
When we mix the generator, we see that the value of the amplitude slot is printed six times, once
for each midi-note object that was created as specified by the note slot. Notice that the value of
the amplitude slot is a random value between .5 and .9.
0.7422715057474961
0.6896894376532909
0.719959111921413
0.7616195154592759
0.5545485937121298
0.7427924428932228
Example 7.3.2 explores the optional argument exclude. In this case, we exclude the previous
value of the amplitude slot to prevent direct repetition.
0.6944736566291632
0.5009347789006956
0.848018833152985
0.5935138651980345
0.5657223829361095
0.7318362501111557
You may think that it is strange that the generator print and the algorithm print-again contain
one PRINT function that is evaluated six times. The reason for multiple evaluations of PRINT is
that algorithms and generators have an implicit looping behavior. Algorithms and generators
loop until they reach a specified end, length, or prescribed number of note events.
It would be useful to exercise more control over the manner in which items are printed to your
monitor. For example, you may wish to see the value of all of the slots on one line of output and
format the floating point value of the amplitude slot so that there are only three positions to the
right of the decimal point. You may do this using FORMAT.
Returns the interpolated y value of x in env with optional :scale and :offset values applied:
f(x)*scale+offset. The type of the value returned normally depends on the type of the arguments
specified to the function. Use :return-type to force the return value to be a specific type, either
float, integer or ratio. float may also be specified as a list (float digits) in which case the floating
point return value will be rounded to digitnumber of places.
interp returns an interpolated value in a range as specified by env. Optionally, we can scale the
value that it returned or add an offset to it. We may use the optional keyword return-type so that
interp returns a floating point value, integer, or ratio.
(READ)
We can allow the user to enter data from the computer keyboard during the evaluation of an
algorithm or generator. Because of the looping behavior of algorithms and generators, Common
Music evaluates the READ function as many times as the generator or algorithm loops. Consider
the following example that assigns the note slot using READ.
The generator read creates 5 midi-notes. The channel, amplitude, duration, and rhythm slots are
assigned when the container is initialized. The body of the generator consists of the Common
LISP FORMAT function that prints a user prompt to the computer monitor. The READ function
accepts input from the keyboard and that input is assigned to the note slot. Mixing the generator
yields the following:
Example 7.4.2
Stella [Read]: mix
8.1 SETF
In Chapter 4, we used the Common LISP macro SETF to assign a value to the Common Music
global variable *standard-tempo*. We viewed the scope of the variable *standard-tempo* as
global because its value is referenced by Common Music when calculating relative rhythms and
durations in containers. The scope of a variable is the region in which a variable's value is
known.
The Common LISP macro SETF template is:
For example, (SETF A 1 B 2 C 3) will assign the variable A the value of 1, B the value of 2,
and C the value of 3.
Example 8.1.1 shows the variable *standard-tempo* evaluates to 60.0. We define a Common
LISP function DOUBLE-THE-TEMPO that doubles a tempo using the variable TEMPO in the
function's argument list. The body of the function returns twice its input. When we call the
function with an input of *standard-tempo*, the doubled value of *standard-tempo* 120.0 is
returned. A query of the value of *standard-tempo* indicates that the global variable has not
been reassigned. A query of the value of TEMPO indicates that the symbol tempo has no value.
What does this mean?
Example 8.1.1
Stella [Top-Level]: ,*standard-tempo*
60.0
Example 8.1.1 demonstrates some of the differences between local and global variables. The
variable TEMPO in the function DOUBLE-THE-TEMPO is a local variable.TEMPO is considered a
local variable because its value is known only within the scope of its function. We demonstrate
the scope of TEMPO by calling the function DOUBLE-THE-TEMPO. We affirm that TEMPO is local
to the function DOUBLE-THE-TEMPO because Common Music knows nothing of its value.
Example 8.1.2 uses SETF in the body of the function definition to reassign the value of
*standard-tempo*. A function call demonstrates that SETF reassigns the value of the global
variable *standard-tempo*.
Example 8.1.2
Stella [Top-Level]: (defun double-the-tempo-with-setf (tempo)
(setf *standard-tempo* (* tempo 2)))
DOUBLE-THE-TEMPO
Stella [Top-Level]: (double-the-tempo-with-setf *standard-tempo*)
120.0
In Example 8.1.2, we use SETF in the body of the function definition to reassign the global
variable *standard-tempo*.
In Example 8.1.3, the second input to SETF is a function call to DOUBLE-THE-TEMPO, WHICH
again doubles the tempo.
Example 8.1.3
Stella [Top-Level]: (setf *standard-tempo*
(double-the-tempo *standard-tempo*))
240.0
In Chapter 5, we used SETF to assign values to the slots of midi-notes.SETF is also used to write
values to the slots of a class. We should be careful not to confuse assigning values to the slots of
a class with assigning values to global variables. The following example demonstrates that
Common Music uses SETF to assign values to slots and that those values are local to the
container that holds those objects.
#<GENERATOR: Local-Vs-Global>
Stella [Top-Level]: mix local-vs-global
Start time offset:(<cr>=None)
Notice in Example 8.1.4, the rhythm slot was calculated based on the current value of *standard-
tempo* which is 240 bpm.
Examples 8.2.1 through 8.2.3 may be evaluated in either Common LISP or Common Music.
In Example 8.2.1, the user-defined function AVERAGE-OF-THREE uses LET to calculate the
average of three numbers passed as arguments to the function.
? (average-of-three 1 2.5 4)
(THE AVERAGE OF 1 2.5 4 IS 2.5)
We enter the body of the function with three arguments. A LET is used to assign the local
variable SUM the sum of the three arguments.LIST is used to present the evaluation as a list by
combining the quoted symbols THE, AVERAGE, OF and IS with the evaluation of (/ SUM
3.0).
LET is an interesting function because the variable value assignments occur simultaneously
rather than sequentially. Example 8.2.2 illustrates this point. The user-defined function MORE-
AVERAGING takes the average of a list of three numbers and two random numbers generated
based on an exclusive upper-bound entered at the function call. The local variables SUM-OF-
NUMBERS and SUM-OF-RANDOM-NUMBERS are assigned at the same time.
Suppose you want to calculate a running average. That is, you average two numbers, and add the
third number to that average and recalculate the average. In order to complete such a
calculation, the variables must be assigned sequentially. The Common LISP function LET*
allows you to assign local variables sequentially. The template for LET* is :
(VARIABLE-2 VALUE-2)
(VARIABLE-N VALUE-N))
(BODY-OF-LET))
? (running-average 1 2.5 4)
count is a Common Music variable that is local to a generator or algorithm. The variable count is
incremented for each note event of a container and ranges in value from 0 to the length -1. For
example, if a container has a length of five midi-notes, count ranges in value from 0 to 4.
time is a Common Music variable that is also local to a generator or algorithm. The variable time
is incremented based on the rhythm of each note or rest event.time begins at 0 and increases by
seconds expressed as a floating point value from the start of the container.
Example 8.3.1 illustrates the Common Music variables count and time by creating a generator
that monitors their changing values using FORMAT.
#<GENERATOR: Monitoring-Count-And-Time>
Stella [Top-Level]: mix monitoring-count-and-time
Start time offset:(<cr>=None)
The current value of count is 0 and the current value of time is 0.0
The current value of count is 1 and the current value of time is 0.2
The current value of count is 2 and the current value of time is 0.4
The current value of count is 3 and the current value of time is 0.6
The current value of count is 4 and the current value of time is 0.8
In Example 8.3.1, we initialize the generator to have a length of 5 notes and end at 3 seconds.
Notice that the length initialization has higher precedence than the end container initialization
since we stop after five notes, long before three seconds have elapsed.
How can you integrate local variables into Common Music's containers? Examples 8.3.2-8.3.3
use LET and LET * to calculate and assign local variables. The slot assignments occur in the body
of the LET or LET*.
In Example 8.3.2, we use the local variable count to assist in calculating the value of the
amplitude slot. We calculate the amplitude by incrementing count by one and dividing that sum
by 5.3. Notice that the amplitude slot assignment occurs in the body of the LET. To help us keep
track of the value of the local variable count, we use the Common LISP primitive PRINT.
#<GENERATOR: Let-Example>
Stella [Top-Level]: mix let-example
Start time offset:(<cr>=None)
0
1
2
3
4
Example 8.3.3 uses LET* to calculate values for the rhythm and duration slots. The duration slot
is calculated by dividing the sum of count plus one and 2 raised to the power of count. The
Common LISP primitive EXPT is used for exponentiation.
#<GENERATOR: let*-Example>
Stella [Top-Level]: mix let*-example
count=0 time=0.0
count=1 time=1.0
count=2 time=2.0
count=3 time=2.7
count=4 time=3.2
Like LET*, vars assigns the variable-value pairs sequentially. Notice that vars does not require
an additional pair of parentheses surrounding the variable-value pairs as LET and LET * do.vars
assigns the values to the variables once when the container is scheduled to run.
Example 8.4.1 is a simple that uses vars. Two local variables, note-value and amplitude-value,
are created and assigned when the container is mixed. The values of the local variables are
assigned to their respective slots. Notice that the slots are assigned outside of the vars
declaration in contrast to Examples 8.3.2 and 8.3.3 using LET and LET*.
Example 8.5.2 uses LET and READ in a Common Music generator. We use LET to assign the
input from READ to the local variables THE-NOTE and THE-AMPLITUDE. We assign the values
of these local variables to their respective slots.
A Common LISP form that assigns a variable creates a lexical closure .SETF, DEFUN, and LET
are Common LISP forms that create lexical closures.
A lexical closure determines the scope of a variable. Assigning a variable using SETF at the
interpreter prompt creates a lexical closure for a global variable. Assigning a variable using LET
creates a lexical closure for a local variable. The value of a local variable is not known outside of
its lexical context.
Examples 8.6.1 through Example 8.6.11 are entered at the Common LISP ? prompt.
Example 8.6.1
? a
> Error: Unbound variable: A
> While executing: "Unknown"
> Type Command-/ to continue, Command-. to abort.
> If continued: Retry getting the value of A.
See the Restarts... menu item for further choices.
1 >
In Example 8.6.2, we assign the variable A the value of 1 using SETF at the Common LISP ?
prompt. Using SETF at the Common LISP ? prompt or Common Music's Top-Level creates a
lexical closure for a global variable. Notice that the variable A is not surrounded by asterisks.
The asterisks do not make a variable a global variable. The asterisks surrounding a global
variable name are a programming convention so the programmer can readily see the scope of a
variable. The way a variable is assigned determines its scope.
Example 8.6.2
? (setf a 1)
1
We can query the value of the variable A by typing its name at the Common LISP ? prompt as
seen in Example 8.6.3.
Example 8.6.3
? a
1
The Common LISP macros INCF and DECF increment and decrement a variable, respectively.
Example 8.6.4
? (incf a)
2
? a
2
? (decf a)
1
? a
1
In the case of Example 8.6.4, INCF and DECF increment and decrement the global variable A.
In Example 8.6.5, LET creates a lexical closure for the variable B. B is assigned using LET and
incremented using INCF. Notice how the value of B is not known by the interpreter. In the case
of Example 8.6.5, INCF increments the local variable B.
Example 8.6.5
? (let ((b 2))
(incf b))
3
? b
> Error: Unbound variable: B
> While executing: "Unknown"
> Type Command-/ to continue, Command-. to abort.
> If continued: Retry getting the value of B.
See the Restarts... menu item for further choices.
1 >
Example 8.6.5 demonstrates how LET creates a lexical closure for the variable A. Recall from
Example 8.6.4 that the global variable A has a value of 1.LET assigns the local variable A the
value of 3. The body of the LET contains a SETF that increments the value of the local variable A
by one. The SETF is contained within the body of the LET. The form returns 4. We query the
value of A at the Common LISP ? prompt and see that the global variable A has retained its value
of 1. Example 8.6.5 demonstrates how two variables of the same name have different lexical
contexts.
Example 8.6.5
? a
1
Example 8.6.6
? (setf c 0)
0
The definition of the function INCREASE results in compiler warnings because the variable C is
not known within the lexical context of the DEFUN.
In Example 8.6.7, the function call to INCREASE is not accompanied by a warning and the value
of C is increased by 3.INCREASE adds 3 to the value of the global variable C.
Example 8.6.7
? (increase 3)
3
? c
3
A function definition that generates warnings but still executes properly is considered poor style.
It is an indication that the programmer may not fully understand the lexical context of the
variables. The programmer must carefully consider the lexical context of variables and the
lexical closures that are created. Example 8.6.8 shows an improvement of the definition of the
function INCREASE.
Example 8.6.8
? (defun increase (c x)
(setf c (+ c x)))
INCREASE
In Example 8.6.9, we call the function INCREASE with inputs of C and 3. The value of the global
variable C is used in the evaluation.
Example 8.6.9
? (increase c 3)
6
A query to the interpreter shows that the global variable C still has a value of 3. The global
variable C has not been reassigned because the SETF is confined to the lexical closure created by
defun with inputs of C and X.
Example 8.6.10
? c
In order to reassign the global variable C, we need to assign it in lexical context in which it was
created as seen in Example 8.6.11.
Example 8.6.11
? c
6
Examples 8.7.1 through Example 8.7.12 are entered at the Stella command interpreter.
Example 8.7.1 creates a generator named scope that contains five midi-notes. All slots are
assigned at container initialization except for the note slot. A SETF inside the generator assigns
the variable X a value of 60. We assign the note slot the value of X using SETF.
Example 8.7.1
(generator scope midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(setf x 60)
(setf note x))
;Compiler warnings :
; Undeclared free variable X (2 references), in an anonymous lambda form inside an anonymous lambda form.
#<GENERATOR: Scope>
Variable X generates two undeclared free variable warnings. Both SETF and the generator create
anonymous lambda forms resulting in the compiler warning "Undeclared free variable X (2
references), in an anonymous lambda form inside an anonymous lambda form."
The generator scope is mixed and the note slot is assigned a value of 60.
Example 8.7.2
We query the value of the variable X at the interpreter and a value of 60 is returned.
Example 8.7.3
Stella [Scope]: ,x
60
The SETF inside the generator creates a global lexical context. The variable X is referenced when
the note slot is assigned.
Example 8.7.4
Stella [Scope]: (setf x 61)
61
Next, we create a generator and reference the global variable X within the body of the generator.
Example 8.7.5
(generator scope midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(setf note x))
;Compiler warnings :
; Undeclared free variable X, in an anonymous lambda form inside an anonymous lambda form.
#<GENERATOR: Scope>
Stella [Scope]: ,x
61
The global variable X is referenced in assigning the note slot but not without generating a
warning.
Example 8.7.6 is a better way to assign a variable to a slot. By using the lexical closure of a LET,
no warnings are generated.
Example 8.7.6
(generator scope midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(let ((y 61))
(setf note y)))
#<GENERATOR: Scope>
Example 8.7.7 further demonstrates the concept of lexical closure. A LET within the generator
scope creates a lexical closure. The local variable Y is assigned a value of 61. Within the body of
the LET, the variable Y is incremented using SETF. While still in the body of the LET, the note
slot is assigned the value of Y. No warnings are generated when scope is evaluated. At the
interpreter, the symbol Y has no value.
Example 8.7.7
(generator scope midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(let ((y 61))
(setf y (+ 1 y))
(setf note y)))
Stella [Scope]: ,y
The symbol Y has no value.
Example 8.7.8 demonstrates lexical closure using DEFUN. The body of the function definition
uses SETF to increment the variable Z.
Example 8.7.8
Stella [Scope]: ,a
1
In Example 8.7.10, the function ADD-ONE is called with an input of A. The variable A references
global variable A and uses the value 1. The function ADD-ONE returns 2. Global variable A retains
its value of 1.
Example 8.7.10
Stella [Scope]: (add-one a)
2
Stella [Scope]: ,a
1
Example 8.7.11 demonstrates a function call to ADD-ONE with an input of 60 to assign the note
slot.
Example 8.7.11
(generator scope midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(setf note (add-one 60)))
Section 8.4 discussed the Common Music declaration vars to assign variables that are local to a
Common Music container. Example 8.7.12 demonstrates how vars sequentially assigns its
variables by first assigning the variable h a value of 60 and then calling the user-defined function
ADD-ONE with an input of h and assigning the result to the variable h. The note slot is assigned
the value of h.
Example 8.7.12
(generator vars midi-note (length 5 amplitude .75 rhythm .35 duration .2 channel 0)
(vars (h 60)
(h (add-one h)))
(setf note h))
Stella [Scope]: ,h
The symbol H has no value.
type refers to one of the item stream data types.pattern refers to one of the item-stream-pattern
types.items refers to the list to be converted into an item stream.
Example 8.8.1 converts the list (c4 d4 e4 fs4 r) into a cyclic note stream.
Example 8.8.1
(make-item-stream 'notes 'cycle '(c4 d4 e4 fs4
r))
You may read an item stream using the Common Music function read-items.
Example 8.8.2 converts a list to a cyclic note stream and assigns the item stream to the global
variable MY-ITEM-STREAM. We use read-items to see the value of MY-ITEM-STREAM.
Example 8.8.2
Stella [Top-Level]: (setf my-item-stream (make-item-stream 'notes 'cycle '(c4 d4 e4 fs4 r)))
#<CYCLIC-NOTE-STREAM #x4ECE54E>
Example 8.8.3 shows an attempt at creating an item stream and using it a generator. We use the
item-stream-accessor item to access one item at a time from the item stream. Notice that the
generator without-vars only outputs the note C4. This is because the item stream is created each
time the generator loops to output an event. Consequently, the only note that plays is C4.
In summary, we have seen how Common LISP forms such as LET and DEFUN create lexical
closures. Variables assigned using SETF, INCF, or DECF create a lexical closure dependent on
the context in which the variable was created. The Common Music declaration vars creates and
assigned variables that are local to a container.
Chapter 9: Conditionals
Evaluating data and making decisions is an important part of describing music algorithmically.
In this chapter, we will learn how to make decisions using Common LISP functions that are
categorized as conditionals . Conditionals choose an action based on an evaluation. Using
Common LISP conditionals, we can write Common Music programs that create music based on a
condition or set of conditions.
9.1 IF
IF is a Common LISP function that is followed by a test clause that evaluates to T or NIL. When
t h e test-clause evaluates to T, the T-consequent clause is evaluated. When the test-clause
evaluates to NIL, an optional NIL-consequent clause is evaluated. If the NIL-consequent clause
is omitted, the program continues with the next Common LISP form.
In Example 9.1.1, we use SETF at the Common LISP ? prompt to assign a global variable *MIDI-
NOTE* a value of 60. We would like to assign a global variable *VELOCITY* a value of .9 if
*MIDI-NOTE* is less than 60. Otherwise, we will assign *VELOCITY* a value of .5. Example
9.1.1 illustrates the conditional assignment of the variable *VELOCITY*.
Example 9.1.1:
? (if (< *midi-note* 60) (setf *velocity* .9) (setf *velocity* .5))
.5
In Example 9.1.1, the test-clause (< *MIDI-NOTE* 60) uses the relational operator < to see if
the current value of *MIDI-NOTE* is less than 60. Because *MIDI-NOTE* was assigned a value
of 60, the test-clause evaluates to NIL and the NIL-consequent clause is evaluated. The
evaluation of the NIL-consequent clause assigns .5 to *VELOCITY*.
IF may be nested to make more complex decisions. The nesting of IF is accomplished when
another IF takes the place of a NIL-consequent clause.
In Example 9.1.2, we write a Common LISP function TEST-RANGE that uses a nested IF to
determine if the variable A-NOTE is within the range of the MIDI Specification.
Example 9.1.2
(defun test-range (a-note)
(if (< a-note 0) 'too-low
(if (> a-note 127) 'too-high 'in-range)))
Given a midi-note input of -5, the function TEST-RANGE evaluates the test-clause "is -5 less
than 0?" The test-clause evaluates to T and the quoted object 'TOO-LOW is returned by the
function.
Given a midi-note input of 129, the function TEST-RANGE evaluates the test-clause "is 129 less
than 0?" The test-clause evaluates to NIL and program control transfers to the next test clause
"is 129 greater than 127?" The test-clause evaluates to T and the quoted object 'TOO-HIGH is
returned by the function.
Given an input of 60, the function TEST-RANGE evaluates the test-clause "is 60 less than 0?"
The test-clause evaluates to NIL and program control transfers to the next test clause "is 60
greater than 127?" The test-clause evaluates to NIL and the quoted object 'IN-RANGE is returned
by the function.
9.2 COND
COND is a Common LISP macro used for multiple test-consequent clauses.COND is comparable to
a nested IF in that it allows the programmer to establish multiple test-consequent pairs.
Quite often, the last test clause of COND is simply T that allows the last consequent clause to be
used as a default action should no other test clauses be true. Once COND finds a test clause that
evaluates to T, program control transfers to the next Common LISP form.
In Example 9.2.1, we write a Common LISP function TEST-RANGE-WITH-COND that uses COND
to determine if a MIDI-NOTE as input to the function is within the range of the MIDI
Specification. Example 9.2.1 is comparable to Example 9.1.2 that uses a nested IF.
Example 9.2.1
Given a midi-note input of -5, the function TEST-RANGE-WITH-COND evaluates the first test
clause "is -5 less than 0?" The test clause evaluates to T and the quoted object TOO-LOW is
returned by the function.
Given a midi-note input of 129, the function TEST-RANGE-WITH-COND evaluates the first test
clause "is 129 less than 0?" The test clause evaluates to NIL and program control transfers to the
next test clause "is 129 greater than 127?" The test clause evaluates to T and the quoted object
TOO-HIGH is returned by the function.
Given an input of 60, the function TEST-RANGE-WITH-COND evaluates the test clause "is 60
less than 0?" The test clause evaluates to NIL and program control transfers to the next test
clause "is 60 greater than 127?" The test clause evaluates to NIL and program control transfers
to the next test clause. T evaluates to itself and the quoted object IN-RANGE is returned by the
function.
9.3 CASE
CASE is a Common LISP macro that implements multiple test-consequent clauses.CASE is
similar to COND but CASE evaluates a key form and allows multiple consequent clauses based on
the evaluation of that key form.
Example 9.3.1
(1 'too-low)
(2 'too-high)
(3 'in-range))))
First, MIDI-RANGE-WITH-CASE enters the body of a LET. The value returned by the nested IF
is assigned to the variable RESULT. If the input is less than 0, the variable RESULT is assigned a
value of 1. If the input is greater than 127, the variable RESULT is assigned a value of 2. If both
conditions evaluate to NIL, RESULT is assigned a value of 3. The variable RESULT is used as the
key-form for the CASE returning a quoted object that corresponds to value of RESULT.
Example 9.4.1 is an example of the Common Music macro unless-resting. If Common Music is
not generating a rest, the amplitude, rhythm, and duration slots are assigned.
2. #<REST 0.4#x4BA86CE>
3. #<REST 0.4#x4BA8716>
4. #<MIDI-NOTE |FS5| 0.500| 1.000| 0.500| 0|>
5. #<MIDI-NOTE |FS5| 0.600| 1.100| 0.600| 0|>
Example 9.4.2 demonstrates the Common Music macro unless-chording. If Common Music is not generating a
chord, the amplitude slot is assigned.Example 9.4.2: unless-chording.lisp
The Common Music function status? operates behind the scenes of the Common Music macros we just discussed
to see if a container is resting, chording, or ending. The template for status? is:
(status? state)
state is a keyword argument that describes the status. Some of the possible states are :resting,
:chording, or :ending. Example 9.4.3 uses the Common Music function status? to FORMAT if
Common Music is generating a chord.
The container cond is initialized so that each note event is output on MIDI channel 0 and there
are 99 note events. A COND is used to test the value of the Common Music variable count which
increments from 0 to 98. The total number of events is divided into three test clauses for COND.
In the first COND clause, if count is less than 33, the note slot will be assigned a value from the
pitch set c4 d4 g4 a4 using the heap pattern type. In the second COND clause, the note slot will
be assigned based on the same pitch set but transposed up a major second. The third COND
clause implements the default case and assigns the note slot based on the same pitch set but
transposed down a major second from the original set.
An IF is used to determine the set of amplitudes for any particular note event. The value of the
variable count is tested and if we are in the first half of the container, an amplitude from the set
.1 .3 .5 is assigned. If we are in the second half of the container, an amplitude from the set .3 .5 .7
is assigned. This algorithm allows the musical material to grow in amplitude as the number of
note events increase.
In Example 9.5.2, we create a musical gesture for a duration of ten seconds that changes pitch
content and amplitude during each second of the container.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/cond.mp3] cond.mp3
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/case.mp3] case.mp3
The generator case is assigned container initializations for start and end. Additionally, channel,
duration, and rhythm slots are assigned static values in the container initialization list. The key-
form of the CASE is obtained by ROUND ing the value of the variable time to achieve an integer
that is used as the key for CASE. Each key in the CASE has two clauses to be evaluated that result
in an assignment to the note and amplitude slots. In this particular example, the resultant music
creates a gradually rising chromatic figure that increases in pitch and loudness as time
progresses.
Forte grouped pc sets by cardinality . The cardinality of a set is the number of elements in that
set. Within each cardinality, pc sets are assigned a unique integer identifier for the prime form
of a set. The prime form of a set is the arrangement of pitch classes such that the smallest
intervals are at the beginning of the set, the interval between the first and last members of the
set is smaller than the interval between the last and first members of the set, and the first pitch
class is 0. For example, set 4-1 is comprised of pitch classes (0 1 2 3). The 4 in the pc set name
represents the cardinality of the set. The 1 is the unique integer identifier associated with that
set. Only pc set 4-1 is comprised of a succession of three minor seconds.
APPEND may take two or more lists as input and return a list of all of the elements of the first list followed by all of
the elements of the second. When appending lists, the template for APPEND is:
(APPEND LIST LIST)
In the following example, we assign pitch class sets to two global variables 6-1 and 5-2. We use
APPEND to create a list of the two pc sets in succession.
Example 10.2.1
? (setf 6-1 '(0 1 2 3 4 5))
(0 1 2 3 4 5)
In Example 10.2.2, we use major triads on C, F and G as sets. A major triad corresponds to set 3-
11 (0 3 7). You may look at the pitch classes and see a c-minor triad or C E-flat G. In set theory,
the major and minor triads are equivalent because they reduce to the same prime form. The
Common Music declaration vars assigns the lists of symbolic note names to variables that are
local to the container. The variables that represent the lists are appended and converted into a
cyclic item stream using make-item-stream.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sets.mp3] sets.mp3
The Common LISP primitive REVERSE makes a retrograde of its input. The template for REVERSE is:
(REVERSE LIST)
Example 10.2.3
? (reverse '(0 1 2 3 4 5)
(5 4 3 2 1 0)
What's the difference between the Common LISP primitive REVERSE and the Common Music
macro retrograde ?REVERSE expects a list as input whereas retrograde expects an item stream.
In Example 10.2.4, we use the Common Music function make-item stream to create an item
stream of the pitch classes in set 6-1. The Common Music macro retrograde reverses the order of
elements in the item stream. The Common Music function read-items shows the result of the
retrograded item stream.
Example 10.2.4
The Common LISP primitive NTH returns a specified element of a list- the nth element of a list. The template for
NTH is:
(NTH INDEX LIST)
where INDEX is the zero-based index that accesses elements in the list.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/reverse.mp3] reverse.mp3
Example 10.2.6
? (nth 4 '(0 1 2 3 5))
5
In Example 10.2.7, we use NTH to randomly access elements in a set.LET* is used to assign the
local variables INDEX, 6-Z36, and OCTAVE. These variables are used to determine the value of
the note slot.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/nth.mp3] nth.mp3
The Common LISP predicate MEMBER checks to see if an element is included in a list. If the
element is not found in the list, MEMBER returns NIL. If the element is found, MEMBER returns a
subset beginning with the found member.
Example 10.2.8
? (member 6 '(0 1 2 3 5))
NIL
Why is MEMBER considered a predicate if it returns a sublist? Recall that in Common LISP, any
non-NIL value evaluates to T.
Example 10.2.9 creates a merge container of generators melody, accompaniment, and final-
chord.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/member.mp3] member.mp3
(if (and (>= time 5) (< time 10)) (setf note (item
(items (chord 60 61 62 63 65 66))))
;;; Chord based on 6-Z3
(if (and (>= time 10) (< time 15)) (setf note
(item (items (chord 70 71 72 75 78))))
;;; Chord based on 5-Z38
(setf amplitude (item (items .01 .02 .03 .04 .05 .06 .07 .08 .09 .01 .02 .03 .04 .05 .06 .07 .08 .09 .1 .2 .3 .4 .5 .1 .2 .3 .4 .5 .1 .2 .3
.4 .5 .1 .2 .3 .4 .5 .1 .2 .3 .4 .5 .6 .7 in sequence)))
(setf rhythm (item (rhythms q q. h h. in heap)))
(setf duration .1))
The Common LISP primitive INTERSECTION takes two lists as input and returns the
intersection of the two sets, that is, a list of the elements common to both sets.
Example 10.2.10
The Common LISP primitive UNION takes two lists as input and returns the elements that are
found in either set.
Example 10.2.11
? (union '(0 1 2 3 5 6) '(0 1 2 3 4 7))
(6 5 0 1 2 3 4 7)
Example 10.2.12 demonstrates how the Common LISP set operations INTERSECTION and
UNION may be used in algorithmic composition. We use vars to assign pc sets to their set names.
Within the vars, we take the INTERSECTION and UNION of set combinations, convert the list to
an item stream using make-item-stream, and assign the result to a variable. We use the item
stream accessor item to select an item from the item streams and assign that item to a slot.
Example 10.2.13
? (set-difference '(0 1 2 3 5 6) '(0 1 2 3 4 7))
(6 5)
The Common LISP predicate SUBSETP accepts two lists a input and returns T if the first list is a
subset of the second and NIL if not.
Example 10.2.14
10.3 Tables
Tables are built in Common LISP by making lists of lists. In fact, a table can be thought of as a
nested indexed list. In Common LISP, sometimes tables are referred to as association lists or
simply a-lists. Consider the table in Figure 10.3.1 that makes a correspondence between pitch
class and note name.
0 C
1 C-sharp
2 D
3 D-sharp
4 E
In Figure 10.3.1, we refer to the pitch class as the key to the table and the note name as its value.
For example, key 1 has a value of C-sharp.
The way we represent tables or a-lists in Common LISP are as nested lists. Example 10.3.1
converts the table representation of Figure 10.3.1 to a nested list and assigns it to the global
variable *SIMPLE-TABLE*.
Example 10.3.1
? (setf *simple-table*
'((0 C)
(1 C-sharp)
(2 D)
(3 D-sharp)
(4 E)))
Example 10.3.2 demonstrates that Common LISP has stored our table as a nested list.
Example 10.3.2
? *simple-table*
((0 C) (1 C-SHARP) (2 D) (3 D-SHARP) (4
E))
Now that we have a table stored in memory, we can look-up things in the table. Common LISP
performs table look-up using the function ASSOC. When ASSOC is given a key in a table, it
returns a list comprised of the key and its corresponding value(s). It may be helpful to think of
ASSOC as returning a specified row in a table as a list.
Example 10.3.2
? (assoc 2 *simple-table*)
(2 D)
If we want to find the value associated with a particular key, we simply use list functions to point
to the element of interest.
Example 10.3.3
? (second (assoc 2 *simple-table*))
D
Performing table look-up is such a common occurrence, we define a Common LISP function
TABLE-LOOK-UP in Example 10.3.4 to simplify the procedure.
Example 10.3.4
? (defun table-look-up (key table-name)
"a generic function to access the second element in a table given its key"
(second (assoc key table-name)))
Example 10.3.5
? (table-look-up 2 *simple-table*)
D
Example 10.3.6 demonstrates how you can use tables into Common Music. We assign a table
named table using vars. Notice the syntactical difference between assigning a table using SETF
and vars. Like LET and LET*,vars requires the variable value pairs be enclosed in parentheses.
After table is assigned using vars, we enter a LET* that randomly generates a pitch class in the
range 0-11. The randomly-generated pitch class serves as the key for table look-up. We assign
the result of the table look-up to the note slot in the body of the LET* .
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table.mp3] table.mp3
Example 10.3.7 integrates many of the concepts we have learned in this chapter and applies
them to Common Music.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table-with-sets.mp3] table-with-sets.mp3
Table 11.1.1:*MY-MIDI-NOTES*:
60 62 67 69
The *MY-MIDI-NOTES* array has 4 locations identified as Index 0, Index 1, Index 2, and Index
3. The value of the data stored at Index 0 is 60, the value of the data stored at Index 1 is 62, etc.
Example 11.1.2:*MY-MIDI-NOTES-WITH-RHYTHMS*
Row 0 60 62 67 69
Recall from Chapter 4 that Common Music uses the following symbols to represent relative
rhythms:
s = sixteenth note
s. = dotted sixteenth note
e = eighth note
e. = dotted eighth note
Note the use of the colon for an optional keyword. The optional keyword :initial-contents
followed by a value may be used to initialize an array, that is, assign all of its locations the same
value. The optional keyword :initial-contents followed by value(s) may be used to assign the
locations of the array when the array is created.
In Example 11.2.1, we create a one-dimensional array with 4 locations at the Common LISP ?
prompt.
Example 11.2.1
? (make-array 4)
#(0 0 0 0)
In Example 11.2.2, we create a one-dimensional array with 4 locations and each location is
initialized to 0.
Example 11.2.2
? (make-array 4 :initial-element 0)
#(0 0 0 0)
In Example 11.2.3, we create a one-dimensional array with 4 locations where the locations are
assigned initial values of 60, 62, 67 and 69.
Example 11.2.3:
? (make-array 4 :initial-contents'(60 62 67 69))
#(60 62 67 69)
Example 11.2.4:
? (setf *my-midi-notes*
(make-array 4 :initial-contents '(60 62 67 69)))
#(60 62 67 69)
A two-dimensional array may be created by using a quoted list as the size of the array. In
Example 11.2.6, we create a two-dimensional array that has three rows and four columns.
Example 11.2.5
? (make-array '(3 4))
#2a((0 0 0 0) (0 0 0 0) (0 0 0
0))
We extend example 11.2.5 in Example 11.2.6 by creating a two-dimensional array with three rows
and four columns where each location of the array is initialized to 0.
Example 11.2.6
? (make-array '(3 4) :initial-element 0)
#2a((0 0 0 0) (0 0 0 0) (0 0 0 0))
Just as with one-dimensional arrays, two-dimensional arrays may be assigned their initial
contents when they are created.
Example 11.2.7
Example 11.2.8
? (setf *my-midi-notes-with-rhythms*
(make-array '(3 4) :initial-contents
'((60 62 67 69)
(s e s s.)
(s. s. e e.))))
The array may also be assigned to a variable using LET as shown in Example 11.2.9.
Example 11.2.9
? (let ((my-midi-notes-with-rhythms
(make-array '(3 4) :initial-contents
'((60 62 67 69)
(S E S S.)
(S. S. E E.)))))
(print my-midi-notes-with-rhythms))
Example 11.3.1
? (aref *my-midi-notes* 2)
67
Example 11.3.2
? (aref *my-midi-notes-with-rhythms* 2 3)
E.
A generator named array is created that contains midi-notes. The generator is initialized to have
ten note events occurring on MIDI channel 0. Each note event will have an amplitude of .5 and a
duration of .2 seconds. Within the generator, we enter the Common Music vars declaration that
creates and assigns the array my-midi-notes-with-rhythms. It is necessary to use vars because we
want to assign the array contents once when the generator is mixed. After vars, we enter a LET
that assigns values to the variables ROW and COLUMN. A LET is necessary because we want the
values of ROW and COLUMN to change for each note and rhythm slot assignment.(RANDOM 2)
returns either a 0 or 1. By adding 1 to the result, ROW is assigned a value of 1 or 2. Row values of 1
or 2 allow us to access the rhythmic data stored in the array.(RANDOM 4) returns an integer
between 0 and 3 that allows us to access the note data stored in the array. Within the body of the
LET, we assign the note and rhythm slots. The note slot is assigned using AREF to reference ROW
0 and the COLUMN previously assigned. The rhythm slot is assigned using AREF to reference the
randomly-selected row for the same column as the selected note data. The Common Music
function rhythm is used to convert a symbolic rhythm to absolute time based on the value of
*standard-tempo*.
Example 11.4.2 defines a Common LISP function that implements a linear probability
distribution that may be used to reference array locations. The user-defined function CHOOSE-
LARGER is created with one argument corresponding to the size of an one-dimensional array.
We enter a LET that randomly assigns the variables X and Y based on the size of the array. The
values of X and Y are compared and the larger of the two randomly-generated values is returned.
Since this function is biased toward larger numbers, repeated calls to CHOOSE-LARGER result in
a preference for larger numbers over smaller numbers.
In Example 11.4.3, the more-arrays generator contains midi-notes and is initialized with a start
time of 0 and an end time of 10 seconds. Each midi-note is initialized a duration of .23 and
outputs data on MIDI channel 0.vars assigns the ARRAY-NOTE, ARRAY-RHYTHM, and ARRAY-
AMPLITUDE one-dimensional arrays. The rhythm slot is assigned by calling CHOOSE-LARGER
which implements a linear-distribution in the selection of the array index to array-rhythm. The
amplitude slot is assigned in a manner similar to the rhythm slot. A LET randomly assigns the
variable OCTAVE a value of either 0 or 1. Within the body of the LET, CHOOSE-LARGER is called
again to determine the index of ARRAY-NOTE. The value stored in the selected location is
summed with either 0 (* 11 OCTAVE when OCTAVE equals 0) or 11 (* 11 OCTAVE when OCTAVE
equals 1) resulting in random assignment of the octave within a two-octave range.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/more-arrays.mp3] more-arrays.mp3
Recall that the Common LISP primitive SQRT returns the square root of its argument.
Example 12.2.1
? (sqrt 25)
5
Since MAPCAR allows you to map a function onto a list, we can take the square root of each
element of a list by passing SQRT to MAPCAR.
Example 12.2.2
? (mapcar #'sqrt '(25 36 81))
(5 6 9)
Notice the syntax of MAPCAR. The function passed to MAPCAR is preceded by a #'. The argument
to the passed function is a quoted list.
Recall in Chapter 9, Example 9.2.1, we wrote a user-defined function that used COND to
determine if a midi-note is within the range of the MIDI specification.
Example 12.2.3
? (defun range (note)
(cond ((< note 0) 'too-low)
((> note 127) 'too-high)
(t 'in-range)))
Example 12.2.4
? (mapcar #'range '(0 -5 129 127 54))
(IN-RANGE TOO-LOW TOO-HIGH IN-RANGE IN-RANGE)
Since returning a list that describes the range status of list of MIDI notes may not be helpful in
composing music, we alter our function in Example 12.2.3. Any MIDI note outside the range of
the MIDI Specification, is recalculated to fall in range. The new function, called RANGIFY, is
defined in Example 12.2.5.
Example 12.2.5
? (defun rangify (note)
(cond ((< note 0) (+ note (abs note)))
((> note 127) (- note (- note 127)))
(t note)))
The function RANGIFY uses an algorithm that returns 0 for all MIDI notes less than 0 and 127
for all MIDI notes greater than 127.RANGIFY is one of many algorithms that may be used to
correct for values that fall outside of the range of the MIDI Specification.
In Example 12.2.6, we map the function RANGIFY onto a list of values. Notice all values less
than 0 return 0 and all values greater than 127 return 127.
Example 12.2.6
? (mapcar #'rangify '(0 -5 129 127 54))
(0 0 127 127 54)
So far, the functions that we've passed to MAPCAR only have one argument. It is possible to pass
MAPCAR functions that have more than one argument. Consider the function definition and
function call in Example 12.2.7.
Example 12.2.7
? (defun my-transpose (note interval)
(+ note interval))
? (my-transpose 60 -12)
48
? (my-transpose 72 6)
78
The function MY-TRANSPOSE requires two arguments: a note and an interval.MY-TRANSPOSE transposes the
note by the specified interval. In Example 12.2.8, we use MAPCAR to transpose a list of notes by a list of specified
intervals.MAPCAR returns a list of the transposed notes.
Example 12.2.8
? (mapcar #'my-transpose '(20 30 40 50) '(-5 5 -10 10))
(15 35 30 60)
The template for a Common LISP lambda expression looks very much like that of DEFUN. Table
12.3.1 compares and contrasts DEFUN and LAMBDA expressions.
Table 12.3.1
Example 12.3.2
Recall in Example 12.2.8 we used MAPCAR to transpose a list of MIDI notes by a list of intervals.
In Example 12.3.3, we use MAPCAR with a LAMBDA expression to perform the same task.
Example 12.3.3
? (mapcar #'(lambda (note interval)
(+ note interval)) '(20 30 40 50) '(-5 5 -10 10))
(15 35 30 60)
Example 12.3.4 demonstrates the use of MAPCAR and LAMBDA expressions in Common Music.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/mapcar.mp3] mapcar.mp3
Notice that the predicates passed to the primitive are proceeded by #'.
We begin by defining a predicate function that determines whether or not its input is a c-major triad.
Example 12.4.1
? (defun c-majorp (chord)
(not (set-difference chord '(c e g))))
The predicate function C-MAJORP takes the SET-DIFFERENCE (or set subtraction) between the
function input chord and the list '(C E G). If all of the elements of the chord are found in '(C
E G),SET-DIFFERENCE returns NIL. We take the NOT of NIL and the predicate function
returns T
If some of the elements in the input chord are remaining after set subtraction, SET-
DIFFERENCE returns those elements as a list. Recall that any non-NIL value evaluates to T. We
take the NOT of a non-NIL value and the predicate function returns NIL.
The Common LISP primitive COUNT-IF returns the number of occurrences a predicate function
evaluates to T.
Example 12.4.2
? (count-if #'c-majorp '((g e c) (f a c)
(c e g b-flat) (g b d)))
1
Notice that in Example 12.4.2 the predicate C-MAJORP finds one occurrence of the list '(C E
G).
Another Common LISP list-filtering primitive is FIND-IF. As we know from Example 12.4.2,
COUNT-IF counts the number of occurrences a predicate evaluates to T.FIND-IF actually
returns the elements that return a T evaluation.
Example 12.4.3
? (find-if #'c-majorp '((g e c) (f a c)
(c e g b-flat) (g b d)))
(G E C)
The Common LISP primitives FIND-IF and REMOVE-IF-NOT have similar functionality. The
result returned by REMOVE-IF-NOT includes another level of parentheses.
Example 12.4.4
? (remove-if-not #'c-majorp '((g e c) (f a c)
(c e g b-flat) (g b d)))
((G E C))
Example 12.4.5
? (remove-if #'c-majorp '((g e c) (f a c)
(c e g b-flat) (g b d)))
((F A C) (C E G B-FLAT) (G B D))
The Common LISP primitive EVERY also expects a predicate and list as input.EVERY returns T if
every element mapped to the predicate evaluates to T.
Example 12.4.6
? (every #'c-majorp '((c e g) (e g c) (c g e)))
T
In Example 12.4.7, we use a LAMBDA expression to see if every note in a list is within the range of
the MIDI specification.
Example 12.4.7
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/applicative.mp3] applicative.mp3
For example, the six terms followed by the initial two terms of 0 and 1 are:
0 1 1 2 3 5 8 13 . . .
next-term = previous-term - 1
Perhaps the easiest way to begin writing recursive functions is to study recursive functions. After
studying several examples, patterns emerge for different kinds of recursive conditions. This
chapter introduces templates for recursion and gives examples of their use.
Consider the example of decrementing a given number by one until it is equal to 0. For Let's say
that our starting number is 4. Our recursive function should output the values 4, 3, 2, 1, and
DONE as seen in Example 13.2.3.
Example 13.2.3
4
3
2
1
DONE
Consider the guidelines for writing a recursive function in relation to Example 13.2.3. We stop
the recursion when the number is equal to 0 so the END-TEST is (ZEROP X). Our recursive
function returns DONE when it is completed so the END-VALUE is DONE. Each step in the
process subtracts one from the previous term so the reduced-argument is (DECF X ).
Now that we've sketched some guidelines, we're ready to apply the template and write the
function.
Consider the example of creating a list that starts at a particular value and ends after a certain number of elements
have been generated. For example, we want to write a function that starts at MIDI note 60 and generates a list of 6
notes that ascend chromatically. Our recursive function should return:
(60 61 62 63 64 65)
The function requires two inputs: a starting MIDI note value and the number of notes to
generate. We stop the recursion after 6 notes have been generated. We need to decrement the
number of notes generated until it is equal to zero. Our END-TEST is (ZEROP NUMBER-OF-
NOTES). Our recursive function returns the completed list. Each step in the process is to
subtract one from the number of notes and add one to the previous note. Example 13.3.2 is the
solution.
Sometimes, adding a FORMAT statement to a recursive process helps clarify what it going on.
Example 13.3.4 is the same function as 13.3.2 with a FORMAT statement prior to the recursive
call.
Consider the example of counting the number of MIDI notes that exceed the range of the MIDI
specification. Given the list of MIDI notes (87 67 129 776 43), the function should return 2.
The function requires two inputs: a list of MIDI notes and a variable to count the number of
MIDI notes outside of the range. We stop the recursion when we reach the end of the list. Our
END-TEST is (NULL THE-LIST). The recursive function returns the number of notes outside
of the MIDI range. Each step in the process looks at the next element in the list by successively
looking at the FIRST of the REST of the list. A solution is found in Example 13.4.2.
Sometimes, it is helpful to insert a FORMAT prior to the recursive call to track the changing
values of the variables as seen in Example 13.4.4.
Example 13.4.4
(defun recursive-count-outlyers (the-list result)
(cond ((null the-list) result)
((or
(< (first the-list) 0)
(> (first the-list) 127))
(incf result)
(format t "~%result = ~a the-list = ~a" result the-list)
(recursive-count-outlyers (rest the-list) result))
(t (format t "~%result = ~a the-list = ~a" result the-list) (recursive-count-outlyers
(rest the-list) result))))
Let's modify RECURSIVE-COUNT-OUTLYERS in Example 13.3.2 so that the function returns the
first occurrence of a MIDI note number outside of the range of the MIDI Specification. Given the
list of MIDI notes (87 67 129 776 43), the function should return 129. If no MIDI notes are
outside the range of the MIDI Specification, the function returns NIL.
The function requires a list of MIDI notes as input. We stop recursion when we reach the end of
the list. Recursive processing can also stop if we find a MIDI note outside of the range of the
MIDI Specification. Since there are two ways that processing terminates, we have two end tests:
(NULL MIDI-NOTE-LIST) and ((OR (< (FIRST MIDI-NOTE-LIST) 0) (> (FIRST
MIDI-NOTE-LIST) 127)) (FIRST MIDI-NOTE-LIST)). That is why this technique is
called double test tail recursion. Each step in the process is to look at the next element in the list
that is done by successively looking at the FIRST of the REST of the list. A solution is found in
Example 13.5.2.
As we saw before, it is helpful to insert a FORMAT prior to the recursive call. The modified
function appears in Example 13.5.5.
The function requires a number as input that corresponds to the number of terms to generate.
Two conditions terminate the recursion: when the input number is 0 and when it is equal to 1.
These conditions allow us to return the first two terms. The default case of the COND establishes
the relationship between terms by multiple recursion:(+ (FIBONACCI (- X 1))
(FIBONACCI (- X 2))). The function returns the specified number of terms beyond the
initial term of 0. A solution is found in Example 13.6.2.
The output from multiple recursion can at first glance appear baffling. Common LISP uses
stacks to process data. Figure 13.6.1 helps make sense of what LISP is doing when it is given the
function call (FIBONACCI 5). The arrows indicate recursive calls. The squares containing
either a 0 or 1 indicate when the input argument reaches 0 or 1 resulting in no further recursive
calls. The circled numbers indicate the order in which the value of the input argument is printed.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000029.jpg]
Example 13.7.2
BREAK places you in the Common LISP debugger. In the debugger, you can examine what LISP
has placed on the stack using a stack backtrace. You can resume recursion by using continue.
Backtrace and continue are implementation specific so consult your Common LISP manual for
further information.
These Common LISP functions are used in the generator recursion-etude. The function
RECURSIVE-MAKE-A-CHROMATIC-LICK is used to generate the note information, create an
item stream, and assign the item stream to the variable note-list. The function RANGE-
LIMITED-RANDOM-NUMBERS is used to generate a list of 15 random numbers in the range .25 to
.98. The list is assigned to the variable RHYS THAT is used to create a cyclic item stream called
rhy-list. The variable RHYS is used as input to the function COUNT-IN-RANGE as we look for
values between .5 and .8 inclusive. The function returns a number that is used in calculating the
value of the amplitude slot.
#|
Make a chromatic lick for note events
|#
(defun recursive-make-a-chromatic-lick (start number-of-notes)
(cond ((zerop number-of-notes) nil)
(t (cons start (recursive-make-a-chromatic-lick
(incf start) (decf number-of-notes))))))
#|
Define a recursive function range-limited-numbers that generates a prescribed number of random values in a user-specified
range
|#
(defun range-limited-random-numbers
(lower-bound upper-bound how-many)
(cond ((zerop how-many) nil)
(t (cons (+ (random (- upper-bound lower-bound))
lower-bound) (range-limited-random-numbers
lower-bound upper-bound (decf how-many))))))
#|
Define a recursive function count-in-range that returns how many elements in a list are inside of user-specified range (lower
and upper bound inclusive).
|#
(defun count-in-range (the-list lower-bound upper-bound result)
(cond ((null the-list) result)
((and
(>= (first the-list) lower-bound)
(<= (first the-list) upper-bound))
(incf result)
(count-in-range (rest the-list)
lower-bound upper-bound result))
(t (count-in-range (rest the-list)
upper-bound lower-bound result))))
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/recursion-etude.mp3] recursion-
etude.mp3
#|
Create a generator called recursion-etude that uses the three recursive functions: make-a-chromatic-lick.lisp, range-limited-
random-numbers, and count-in-range. These three functions highlight three recursive processes: list consing recursion, single-
test tail recursion, and conditional augmenting tail recursion.
#|
(generator recursion-etude midi-note (channel 0 length 15)
(vars (note-list (make-item-stream 'items 'heap
(recursive-make-a-chromatic-lick 60 12)))
(rhys (range-limited-random-numbers .25 .98 15))
(rhy-list (make-item-stream 'items 'cycle rhys))
(amps (count-in-range rhys .5 .8 0)))
(setf note (item note-list))
(setf rhythm (item rhy-list))
(setf amplitude (* (random amps) .1))
(setf duration rhythm))
Profile for tape by Charles Dodge is a three-voice composition in which the choice of pitch,
timing, and amplitude is determined by application of a 1/f algorithm. The structure of the work
is like a fractal in that recursive processes are used to determine multiple levels of scale and self-
similarity. [Dodge, 1988]
14.2 DOTIMES
Perhaps the simplest iterative form in Common LISP is DOTIMES.DOTIMES, as its name
suggests, performs a specified task(s) a prescribed number of times.DOTIMES increments a loop-
control-variable up to (but not including) an upper-bound. You may optionally specify a return
value when the loop is terminated. The body of the loop specifies the task(s) that should be
completed during each iteration.
Example 14.2.1
? (dotimes (counter 4)
(print counter))
0
1
2
3
NIL
Notice that Example 14.2.1 returns NIL after concluding the iterative process. The reason
DOTIMES returns NIL is because we did not specify an OPTIONAL-RESULT. Example 14.2.2 is
the same as Example 14.2.1 except an optional result is specified. We return the symbol DONE.
Compare Example 14.2.2 with the recursive function in Example 13.2.4.
Example 14.2.2
? (make-a-chromatic-lick 60 6)
Why does the list go from largest to smallest? Because we consed the newly-created element
onto the front of the list. If we want a list in ascending order, we should use the Common LISP
primitive REVERSE on THE-LIST. Compare Example 14.2.4 with the recursive solution found in
Example 13.3.2.
Example 14.2.4
? (make-a-chromatic-lick 60 6)
index = 0 the-list = (60)
index = 1 the-list = (61 60)
index = 2 the-list = (62 61 60)
index = 3 the-list = (63 62 61 60)
index = 4 the-list = (64 63 62 61 60)
index = 5 the-list = (65 64 63 62 61
60)
(60 61 62 63 64 65)
14.3 DOLIST
The Common LISP primitive DOLIST is like DOTIMES, except that it operates on lists. Like
DOTIMES, DOLIST has a loop-control-variable, an optional result, and a body. The loop-control-
variable serves as an index to the list. The loop-control-variable increments until it is equal to
the length of the list.
Example 14.3.1:
(print counter))
60
61
62
63
DONE
Example 14.3.2 defines a function COUNT-OUTLYERS that counts the number of notes in a list
that are outside of the range of the MIDI Specification. We enter a LET and initialize the variable
RESULT to 0.RESULT is incremented when a note exceeds the range of the MIDI Specification.
We initialize the lower and upper bound of the MIDI Specification to 0 and 127, respectively.
The body of the LET is a DOLIST. The DOLIST has a loop-control-variable named ELEMENT that
steps through the list. We use WHEN to test the value of each element of the list with the upper
and lower bounds. If the element exceeds either the lower bound or the upper bound, we
increment the variable RESULT. When the DOLIST completes processing, it returns the value of
the variable RESULT. A FORMAT statement is added to the body of the DOLIST to track the
values of the variables ELEMENT and RESULT. Compare Example 14.3.2 with the recursive
solution found in Example 13.4.4.
14.4 RETURN
Sometimes, it is necessary to exit an iterative process before a loop has repeated a prescribed number of times.
The way to force an early exit from a loop is through the Common LISP form RETURN. The template for RETURN
is:
(RETURN <OPTIONAL-RETURN-VALUE>)
RETURN is generally preceded by a condition that forces an early exit from the loop. If a return-
value is not specified, the loop returns NIL.
Example 14.4.3 is a modification of Example 14.2.2. Instead of counting the notes that fall
outside of the MIDI Specification, the function returns the first occurrence of a note outside of
the range. If no notes in the list exceed the range of the MIDI Specification, DOLIST returns
NONE-FOUND.
14.5 DO
The most powerful iterative form in Common LISP is DO. Because DO is so powerful, its template is complex.
(DO ((VARIABLE-1 INIT-VALUE1 OPTIONAL-UPDATE-VALUE1)
Variables may be initialized in DO and their values optionally updated at each iteration. A LOOP-
TERMINATION-TEST terminates processing. One or more consequences may be evaluated when
the loop terminates. If the condition to terminate processing is false, the body of the DO is
evaluated.
Recall Example 14.2.1 when we used DOTIMES to print the changing value of the loop-control-
variable. Example 14.5.1 implements the same algorithm using DO.
Example 14.5.1
0
1
2
3
DONE
Recall Example 14.2.1 when we used DOLIST to PRINT each element in a list. Example 14.5.2
implements the same functionality using DO.
Example 14.5.2
60
61
62
63
DONE
In Example 14.5.3, we modify Example 14.2.2 to use DO. Notice in this example, we provide
initialization and update values for the variable ELEMENT. The variable RESULT is initialized but
not updated. The reason we do not update RESULT is because we conditionally increment its
value in the body of the DO.
Example 14.5.5
? (make-a-chromatic-lick-do 60 6)
(65 64 63 62 61 60)
14.6 DO*
The Common LISP primitive DO* is like DO except that its variable initializations are done
sequentially. The difference between DO and DO* is analogous to the difference between LET and
LET* .
In Example 14.6.1, we use DO* to implement the same procedure in Example 14.3.3: a function
that returns the first note outside of the range of the MIDI Specification. Just as we saw in
Example 14.3.3, we use a condition followed by RETURN to force an early exit from the loop. We
RETURN the value of the variable ELEMENT that is outside the range.
Why do we need to use DO* rather than DO for Example 14.6.1? Because the variable ELEMENT is
initialized based on the value of the variable INDEX.
LOOP is generally followed by a conditional form such as WHEN or UNLESS to prevent an infinite
loop.
In Example 14.7.1, we once again make a chromatic lick as we did in Examples 14.2.3 and 14.4.5.
This time, we use LOOP.
Example 14.7.1: make-a-chromatic-lick-loop.lisp
(incf index))))
Notice that we enter a LET in the body of the function. We initialize the variable INDEX to 0 and
the variable THE-LIST to the empty list. We enter a LOOP in the body of the LET.LOOP is
immediately followed by a condition where the value of INDEX is compared to the variable
NUMBER-OF-NOTES. If the condition evaluates to NIL, the body of the LOOP is evaluated and we
CONS a note onto THE-LIST and re-assign the-list using SETF. We increment the index using
SETF. The iterative process continues until the INDEX equals the NUMBER-OF-NOTES. When
the INDEX equals the NUMBER-OF-NOTES, the RETURN is evaluated and the LOOP returns the
value of the variable THE-LIST.
? (make-a-chromatic-lick-loop 60 6)
(65 64 63 62 61 60)
In Example 14.7.2, once again we search for MIDI note numbers that are outside the range of the
MIDI Specification as we did in Examples 14.2.2 and 14.4.3. This time, we use LOOP.
In the body of the function, we enter a LET and initialize the variables INDEX and RESULT to 0.
In the body of the LET, we enter a LOOP. The condition that terminates iterative processing uses
UNLESS. Recall that UNLESS evaluates its consequent clause only when the condition is false.
Because element is 0, and the UNLESS is true, we enter the body of the LOOP. Within the LOOP is
a condition that checks to see if the note is in out of bounds. If the note is too high or too low,
the variable RESULT is incremented. The body of the LOOP increments the variable ELEMENT.
The LOOP terminates when the condition that ELEMENT is less than the length of the MIDI note
list is false. The value of the variable RESULT is returned.
(incf element))))
VARIABLE is a variable that is associated with the stream of data that is read from the file.
The following code assumes you are using the Macintosh Operating System (MacOS) and that
the file midi.dat resides on the desktop.
The contents of the ASCII (plain text) file midi.dat looks like this:
60 98 0
34 87 1
98 78 2
WITH-OPEN-FILE accepts the optional keyword :DIRECTION with a keyword value of :INPUT
or :OUTPUT. If no keyword and value is specified, WITH-OPEN-FILE assumes the file is opened
for input.
In Example 14.8.1, we define a function GET-MIDI-DATA that opens the file midi.dat, reads
data from the file, and outputs what it has read to the monitor using FORMAT.
(defun get-midi-data ()
(with-open-file (my-data "Macintosh HD:Desktop Folder:midi.dat")
(let ((the-note (read my-data))
(the-amplitude (read my-data))
(the-channel (read my-data)))
(format t "~%The midi note is ~a the amplitude is ~a and the channel is ~a" the-note the-amplitude the-channel))))
We enter a LET while still in the lexical context of the WITH-OPEN-FILE. The LET assigns the
variables THE-NOTE, THE-AMPLITUDE, and THE-CHANNEL with data read from the file. The
body of the LET uses a FORMAT to print the data read from the file to the monitor.
Example 14.8.2
? (get-midi-data)
As you can see, the function GET-MIDI-DATA only retrieved the first line of data from the file.
We need to use an iterative process to retrieve more than one line of data. Example 14.8.3
defines a function READ-MULTIPLE-RECORDS. The function uses a DO loop to repeatedly
process data from the file. The DO loop assigns the variables MIDI-NOTE, VELOCITY, and
CHANNEL. In this case, both the initialization and update values of the variable in the DO are to
READ from the file. The NIL in (READ MY-DATA NIL) tells LISP to return NIL when it
encounters the end of the file. The loop terminates using the condition (NOT MIDI-NOTE).
Since LISP assigns MIDI-NOTE NIL when it reaches the end of the file, the NOT of NIL is T and
the loop terminates. The body of the DO loop is a FORMAT.
(defun read-multiple-records ()
#|
Open the data file using with-open-file. Associate the stream of data from the file with my-data
|#
(with-open-file (my-data "Macintosh HD:Desktop Folder:midi.dat")
(do ((midi-note (read my-data nil) (read my-data nil))
(velocity (read my-data nil) (read my-data nil))
(channel (read my-data nil) (read my-data nil)))
((not midi-note))
(format t "~%The midi note is ~a the velocity is ~a and the channel is ~a" midi-note velocity
channel))))
Example 14.8.5
? (read-multiple-records)
The midi note is 60 the velocity is 98 and the channel is 0
The midi note is 34 the velocity is 87 and the channel is 1
The midi note is 98 the velocity is 78 and the channel is 2
NIL
Example 14.8.6 opens two files: one for input (midi.dat) and one for output (midi-out.dat).
Common LISP assumes that midi-out.dat does not exist. We use a DO loop to iteratively read
from the input file. We use FORMAT in the body of the DO loop to write to the output file. Notice
that FORMAT does not use T to write to the terminal but instead uses the variable OUT-DATA to
direct printing to the output file.
(defun write-multiple-records ()
(with-open-file (in-data "Macintosh HD:Desktop folder:midi.dat" :direction :input)
(with-open-file (out-data "Macintosh HD:Desktop folder:midi-out.dat" :direction :output)
(do ((midi-note (read in-data nil) (read in-data nil))
(velocity (read in-data nil) (read in-data nil))
(channel (read in-data nil) (read in-data nil)))
((not midi-note))
(format out-data "~&midi-note = ~S velocity = ~S channel ~S" midi-note velocity channel)))))
Example 14.8.7
? (write-multiple-records)
NIL
All of the output from the function has been directed to the file midi-out.dat. When we view the
contents of midi-out.data, we see:
The thread container does not have a looping behavior like a generator or an algorithm. We can
use iterative forms inside a thread container to accomplish repetitive processing. Example 14.9.1
creates a thread named chromatic-lick-stella that contains a DO loop. The variables initialized in
the DO loop are MY-NOTE and MY-RHY that are initialized at 0 and a random floating point value
between 1 and 2.9999, respectively. The loop-termination-test is when the variable MY-NOTE is
greater than 65. When the loop-termination-test is false, a new midi-note object is created using
the Common Music macro object. The note and rhythm slots are assigned the value of the
variables MY-NOTE and MY-RHY. The remaining slots are assigned numeric constants.
Iterative forms like DOLIST are very handy in creating chords using a thread. Example 14.9.2
demonstrates the use of DOLIST to step through a list assigning each element to the note slot.
Because the rhythm slot retains a value of 0, the notes are generated with the same start time.
Playback of the chord may be controlled using the thread initialization parameter start.
Of course, threads may be combined into a merge to control playback of events. Example 14.9.3
is a simple example of how iterative forms may be contained in threads and processed in parallel
using a merge.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/simple-example.mp3] simple-
example.mp3
Probability is the study of outcomes that produce a given event to the total number of possible
outcomes. A probability is the likelihood that a particular event will occur. A probability is
expressed as a ratio- the likelihood that a given event will occur in relation to the total number
of possible outcomes. For example, consider a six-sided die where each side is uniquely
identified 1,2,3,4,5 and 6. The probability that a 1 will be rolled is 1:6. The ratio 1:6 may also be
expressed as a floating point value or .16666 (16.666%). Considering the example of the six-
sided die, the sum of all possible outcomes is 6/6 or 1.0 (100%).
Example 15.2.1
(setf note (item (notes (c4 :weight .5) d4 e4 in
random)))
In Example 15.2.1, the note c4 is 1/2 as likely to be selected as d4 and e4. Given that there are 3
notes and the sum of their probabilities must equal 1 (of 100%), and c4 is 1/2 as likely to be
selected as d4 and e4, Table 15.2.1 shows the probabilities of the note events.
Table 15.2.1
Note Name C4 D4 E4
Weight .2 .4 .4
If a weight is not specified, the random pattern type calculates the probabilities of each event as
being equal. When all events have an equal probability of being selected, white noise results.
White noise has a flat spectrum meaning that its energy is dispersed evenly throughout a
specified range. White noise is also referred to as 1/f 0 noise because of its flat spectrum. In
Example 15.2.2, the range of events are the rhythms for a quarter, sixteenth, and eighth note.
Example 15.2.2
Table 15.2.2 shows that the selection of rhythms based on equal probability.
Table 15.2.2
Rhythm Q S E
The random item stream pattern type also implements the options :min and :max. The option
:min determines how many direct repetitions are allowed before a new element must be selected.
The option :max sets a maximum value an element may be repeated before a new element must
be selected.
Example 15.2.3
(setf amplitude (item (items (.1 :max 1) (.2 :max 1) (.3 :min 2) (.4 :min 2)in random)))
Example 15.2.3 indicates that .1 and .2 can repeat at most one time before a new event must be
selected. .3 and .4 must repeat at least 2 times before a new event is selected.
The random item stream constructor has no recollection of past events. Each time an item is
assigned to a slot, Common Music evaluates the probability and selects an item. The keyword
options :min and :max force Common Music to take previous events into consideration before a
slot is assigned. Because of the evaluation of the probability at every pass of an item stream, and
the memory for past slots as specified by :min and :max, it is possible that mixing a container
will yield results very different from the specified weights. When evaluating Examples 15.2.4,
Common Music returns 3 C4s, 4 E4s, and 5 D4 and 2 quarter notes, 5 eighth notes and 5
sixteenth notes. Notice the amplitude slot is not successively assigned .1 or .2. Also notice that
when .3 and .4 is selected, its value is assigned twice in succession.
The graph in Figure 15.3.1 has three nodes- C4, E4, and DS4. Each node represents a symbolic
note name. The arrows connecting the nodes specify the direction in which the graph may be
traversed. For example, from node E4, we can go to C4 or DS4. From node C4, we can only go to
E4.
Figure 15.3.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000030.jpg]
In Common Music, the relationship between nodes is specified as (start to destination) where
destination may be an item stream. For example,
states that node E4 may traverse to C4 and DS4 in cycle. When the graph arrives at node E4, it
will alternate its destination to either C4 or DS4.
Example 15.3.1
(setf x (notes (c4 to e4)
(e4 to (items c4 ds4 in cycle))
(ds4 to (items c4 e4 in palindrome))
in graph))
In Example 15.3.1, we create a graph item stream comprised of notes and assign it to the variable
X. We may read the item stream using the Common Music function read-items as seen in
Example 15.3.2.
Example 15.3.2
? (read-items x 20)
(C4 E4 C4 E4 DS4 C4 E4 C4 E4 DS4 E4 C4 E4 DS4 E4 C4 E4 DS4 C4
E4)
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/graph.mp3] graph.mp3
Carefully compare the graph in Figure 15.3.1 with the output of the read-items in Example
15.3.2. Notice that the succession of notes follows the specified rules and item stream pattern
types. E4 goes to C4 and DS4 in cycle. DS4 goes to C4 and E4 in palindrome. The nodes C4, E4,
and DS4 are in graph.
Example 15.3.3 incorporates the graph item stream pattern in Example 15.3.1 in a generator.
C4 D4 E4
Table 15.4.1 is an example of a first-order transition table. The current events are listed in the
zeroth column and the possible next events are listed in the zeroth row. We interpret the first
row of the transition table as "if the current event is a C4, there is a 10% chance that another C4
will be selected, a 75% chance that D4 will be selected, and a 15% chance that E4 will be
selected."
Two frequent errors in constructing transition tables are loops and dead ends. The transition
table in Table 15.4.2 will quickly fall into a loop that generates a chain of alternating D4s and
E4s.
C4 D4 E4
C4 ——— .5 .5
A dead end occurs in a transition table when an event is specified and there is no way to select
another event from that event. The transition table in 15.4.3 states that "if the current note is a
C4, there is a 50% chance that D4 will be selected, a 25% chance that E4 will be selected, and a
25% chance that F4 is selected." If F4 is selected, it had no available outputs since there is no
row in the transition table that considers F4 as a current event. The markov chain reaches a dead
end.
C4 D4 E4 F4
The Common Music macro markov may be used to generate an item stream that constructs a
markov chain. The markov macro template is:
Example 15.4.1 uses markov to assign the note slot using the first-order transition table
described in Table 15.4.1.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000031.jpg]
Quite often, markov processes are used to generate music based on a previous composition or
set of compositions. A transition table may be constructed by analyzing the likelihood that a
certain event will be followed by another event. Consider the folk melody Aunt Rhody as notated
in Figure 15.4.2.
There is one occurrence of the note F5 and it always goes to E5. To describe this relationship in a
first-order transition table, we would say given F5, there is 100% change that E5 will be selected.
There are two occurrences of the note G5. G5 may be followed by either another G5 or F5. To describe this
relationship in a first-order transition table, we would say that given G5, there is a 50% chance that another G5
will be selected and a 50% chance that an F5 will be selected.
(G5 -> (G5 0.5) (F5 0.5))
When a transition table is constructed, the sum of the weights for a row equals 1. Common Music evaluates
transition tables when the sum of a row does not equal 1. Under these circumstances, Common Music scales the
probabilities in relation to the other events in the row as was noted in the random item stream pattern. For
example,
(C4 -> (C4 .5) D4 E4)
means that C4 is 1/2 as likely to be selected as D4 and E4 or the probability that given a C4,
another C4 will be selected is 20%. The likelihood that D4 or E4 will be selected is 40%.
The Common Music function markov-analyze analyzes a list and returns both a transition table
and the Common Music code to implement the transition table.markov-analyze may be
accompanied by keywords :order and :sort to specify the order of the transition table Common
Music should return from the analysis and the sequence of events in the table. Example 15.4.2
demonstrates the use of markov-analyze by passing markov-analyze the note list from Aunt
Rhody. We specify that Common Music return a first-order transition table and the table be
arranged according to the list (c5 d5 e5 f5 g5).
Example 15.4.2
? (markov-analyze '(e5 e5 d5 c5 c5 d5 d5 e5 d5 c5 g5 g5 f5 e5 e5 d5 c5 d5 e5
c5)
:order 1
:sort '(c5 d5 e5 f5 g5))
C5 D5 E5 F5 G5
(C5) 0.200 0.400 0.200 ——- 0.200
(D5) 0.500 0.167 0.333 ——- ——-
(E5) 0.167 0.500 0.333 ——- ——-
(F5) ——- ——- 1.000 ——- ——-
(G5) ——- ——- ——- 0.500 0.500
(MARKOV (C5 -> (C5 0.2) (D5 0.4) (G5 0.2) (E5 0.2))
(D5 -> (C5 0.5) (D5 0.16666666666666666) (E5
0.3333333333333333))
(E5 -> (E5 0.3333333333333333) (D5 0.5) (C5
0.16666666666666666))
(F5 -> (E5 1.0))
(G5 -> (G5 0.5) (F5 0.5)))
markov-analyze returns a first-order transition table. The transition table is followed by the
Common Music code that implements the transition table.
Next, we analyze the rhythm of Aunt Rhody using markov-analyze. We specify a second-order
transition table.
Example 15.4.3
? (markov-analyze '(q e e q q q q e e q q e e q q e e e
e h)
:order 2
:sort '(e q h))
E Q H
(E E) 0.333 0.500 0.167
(Q E) 1.000 ——- ——-
(E Q) ——- 1.000 ——-
(Q Q) 0.500 0.500 ——-
(H Q) 1.000 ——- ——-
(E H) ——- 1.000 ——-
The second-order transition table is more complex to read than a first order transition table.
Consider the third row of the transition table in Example 15.4.4.
(Q E -> (Q 1.0))
The row is interpreted as, "if the current rhythm is an eighth note and it was preceded by a
quarter note, return a quarter note."
Given a first-order transition table for the selection of note events and a second-order transition
table for the selection of rhythmic values, we can generate a melody that has the comparable
note and rhythm probabilities as Aunt Rhody.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000033.jpg]
Sometimes, markov-analyze generates transition tables that dead end. These dead ends generate
the error message, "No outputs for past choices." The error message can be avoided by reducing
the order of the transition table or extending the transition table to include possible choices not
returned by markov-analyze.
If you have a large MIDI file you'd like to analyze using markov-analyze, it is easiest to import
the MIDI file into Common Music. Example 15.4.6 assumes a MIDI file named test.midi resides
on the desktop of a Macintosh computer.
Example 15.4.6
Stella [Top-Level]: import "Macintosh HD:Desktop
Folder:test.midi"
We use the newly-imported file that has been named From-Test. We change our current focus
object to From-Test.
Example 15.4.7
Stella [Top-Level]: go 1
Focus: From-Test
Type: Thread
Status: Normal
Objects: 22
Start: unset
In Example 15.4.8, we use the map command to collect all of the note data into a list. We then
use the collected list of note data as input to the markov-analyze function.
Example 15.4.8
Stella [From-Test]: map * collect note
CLAUSE COUNT VALUE
collect note 22 (D5 D5 D5 C5 D5 E5 E5 D5 D5 D5 C5 C5 D5 E5 E5 D5 E5 D5 D5 E5 E5 E5)
Stella [From-Test]: (markov-analyze '(D5 D5 D5 C5 D5 E5 E5 D5 D5 D5 C5 C5 D5 E5 E5 D5 E5 D5 D5 E5
E5 E5) :order 2)
D5 C5 E5
(D5 D5) 0.400 0.400 0.200
(C5 D5) ——- ——- 1.000
(E5 D5) 0.750 ——- 0.250
(D5 C5) 0.500 0.500 ——-
(C5 C5) 1.000 ——- ——-
(D5 E5) 0.250 ——- 0.750
(E5 E5) 0.750 ——- 0.250
Brownian motion in one-dimension can be described by applying a random process to a succession of events in
relation to a number line. Consider a seven-sided die that has values of -3, -2, -1, 0, 1, 2, and 3. After tossing the
die eight times, we derive a number series:
-2, -3, +2, +2, +1, +2, -2, -2
That number series may be mapped to a number line to describe one-dimensional motion along
the number line. Our number line is as follows:
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000034.jpg]
If we assume a starting value of 60, the following number series is returned:
60, 58, 55, 57, 59, 60, 62, 60, 58
60 - 2 = 58
58 - 3 = 55
55 + 2 = 57
57 + 2 = 59
59 + 1 = 60
60 + 2 = 62
62 -2 = 60
60 - 2 = 58
Example 15.1.1 implements this algorithm for one-dimensional Brownian motion resulting in a
note list given a starting position and prescribed number of elements. The Common LISP
function BROWNIAN-MOTION accepts two inputs: the starting position and how many steps it
should simulate. We enter a DO* and initialize the variable COUNTER to 0, incrementing the
variable at each pass of the loop.COUNTER is a loop-control-variable that terminates iteration
when its value is equal to the NUMBER-OF-NOTES. A one-dimensional array named THE-ARRAY
is created and initialized to have seven locations with the values of -3, -2, -1, 0, 1, 2, and 3. We
CONS the variable start onto the variable THE-LIST at the first iteration. For subsequent
iterations, we CONS the newly-calculated note value onto THE-LIST.
In Example 15.5.2, we call the Common LISP function BROWNIAN-MOTION inside the generator
brownian-music.BROWNIAN-MOTION returns a note list that is converted to an item stream and
assigned to the variable brown-item-stream using the Common Music declaration vars. We
assign one item from the brown-item-stream to the note slot.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/brownian-music.mp3] brownian-
music.mp3
Richard F. Voss developed a simple algorithm to simulate 1/f noise [Gardner, 1986]. His
algorithm uses three six-sided dice: one red, one green, and one blue. The sum of the three dice
range in value from 3 to 18 returning 16 possible values. These 16 values may be mapped to any
16 adjacent notes or any sixteen musical parameters.
Voss uses a table similar to that found in Table 15.6.1 to create 1/f music. The numbers 0
through 7, base 10, are located in the left-most column. The three-digit binary equivalent of the
decimal value is found in the right-most three columns. Each binary position is associated with a
die color noted in the column heading.
The algorithm commences by rolling all three dice returning a value between 3 and 18. This
initial action corresponds to Row 0 in the table. When comparing Row 0 to Row 1, we note that
only the red value changes (from 0 to 1). Our corresponding action is to pick up the red die and
throw it calculating a new sum for the three dice. The new sum is used to select the next note of
the composition. We are ready to generate a new note, so we compare Row 1 with Row 2 and
find that both the green and red values change. Our corresponding action is to pick up the red
and green dice and throw them. The dice are summed and the new sum corresponds to another
new note. This process is repeated until the prescribed number of notes is generated.
Table 15.6.1
0 10 = 0 0 0 2
1 10 = 0 0 1 2
2 10 = 0 1 0 2
3 10 = 0 1 1 2
4 10 = 1 0 0 2
5 10 = 1 0 1 2
6 10 = 1 1 0 2
7 10 = 1 1 1 2
The Common LISP function 1-OVER-F accepts as input a NUMBER representing the number of
events it should generate. A DO* begins the iterative process by simulating the initial throw of
the three dice represented by the variables BLUE, GREEN, and RED. The sum of the random
process is assigned to the variable TOTAL that is CONS ed onto THE-LIST. Upon subsequent
iterations of the loop, we simulate the toss of die only if the variable COUNTER has certain values
that correspond to the entries in Table 15.6.1. For example, we toss the BLUE die only if the
COUNTER equals four simulating the change in state from 3 10 to 4 10 in Table 15.6.1.
[] 1-over-f.mp3
We can easily integrate our function to generate a list of notes based on the 1/f algorithm into
Common Music. Example 15.6.2 creates an item stream by calling the Common LISP function
1-OVER-F inside the generator 1-over-f-music. A LET randomly selects the value of OCTAVE in
the range 4 to 5. The value of OCTAVE is multiplied by the value of an item returned from the 1-
over-f-item-stream.
Entropy for computer-controlled piano by Christopher Dobrian [Dobrian, 1991] explores the
perception of randomness and order (entropy and negentropy) in musical structure, and
demonstrates the use of stochasticism not only as a model for the distribution of sounds in time,
but also as a method for variation of a harmonic "order." The composing algorithm takes as its
input a description of some beginning and ending characteristics of a musical phrase, and
outputs the note information necessary to realize a continuous transformation from the
beginning to the ending state. The algorithm composes melodic phrases of any length by
calculating arrays of instantaneous probabilities, and incrementing toward the ending point and
repeating the process.
object represents a Common Music object. In the following examples, we use containers as
objects. Each container has its own initialization parameters that follow the object name.
Example 16.1.1 uses the Common Music function sprout to conditionally create one of two
unnamed threads. The threads are unnamed because the container class thread is followed by
nil. One thread is created when the value of the note slot is either 60 or 64. The other thread is
created when the value of the note slot is 62. When the generator is mixed, the C and E are
harmonized by a C major triad and the D is harmonized by a G7 chord. Although this example is
very simple from a melodic and harmonic standpoint, it illustrates a very powerful feature of
Common Music.
(generator harmonize midi-note (start 0 length 3 channel 0 rhythm 1 amplitude .7 duration .75)
(setf note (item (items 60 62 64)))
(if (or (= note 60) (= note 64))
(sprout (thread nil (start time)
(dolist (chord-member '(48 52 55))
(object midi-note
note chord-member
rhythm 0
duration 1
amplitude .5
channel 0)))))
(if (= note 62)
(sprout (thread nil (start time)
(dolist (chord-member '(47 50 53 55))
(object midi-note
note chord-member
rhythm 0
duration 1
amplitude .5
channel 0))))))
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/harmonize.mp3] harmonize.mp3
16.2 Encapsulation
Encapsulation is the act of placing one thing inside another. In the case of Common Music, we
u s e the term encapsulation to mean that a container is created inside of a Common LISP
function.
In Example 16.2.1, DEFUN is used to define a Common LISP function called ENCAPSULATION.
The function has two required arguments (THE-NAME and REPETITION ) and an optional
argument START-TIME. The optional argument START-TIME has a default value of 0. The
variable THE-NAME is used in conjunction with the Common Music function name to name the
generator. When REPETITION is greater than one, the Common Music function sprout is called
which in turn calls the Common LISP function ENCAPSULATION. The function
ENCAPSULATION is called inside of itself making this an example of recursive encapsulation.
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/encapsulation.mp3] encapsulation.mp3
Example 16.2.3 is taken from the Common Music Stella Tutorial "Describing Music
Algorithmically." This example gracefully demonstrates recursive encapsulation in the creation
of a musical fractal.
(defun sierpinski (nam dur key amp rep &optional (tim 0))
(algorithm (name nam) midi-note (start tim rhythm dur amplitude amp)
(setf note (item (intervals 0 11 6 from key) :kill t))
(when (> rep 1)
(sprout (sierpinski nil (/ rhythm 3) note amp (1- rep) time)))))
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sierpinski.mp3] sierpinski.mp3
Algorithms that output MIDI data may be positioned onto the tracks of a MIDI sequencer. By
positioning the data in a time-domain representation, the composer can readily experiment with
the placement of events in time and the density of those events. A MIDI sequencer allows for
graphic editing of MIDI data so small changes to the output of an algorithm are simplified.
Figure 16.3.1 shows an example of the output of two algorithms positioned in the time-domain
representation of a MIDI sequencer.
Figure 16.3.1
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000035.jpg]
Because Common Music outputs MIDI data as well as data that may be used as input to sound
synthesis languages such as Csound, the composer may wish to assemble a composition using a
digital audio workstation that imports MIDI. Similar to Figure 16.3.1, the user-interface of a
digital audio workstation generally uses a time-domain representation of audio and MIDI
allowing the composer great freedom in the organization of musical events.
Eulogy by Mary Simoni integrates processed speech and algorithmic processes to create a
tribute commemorating the funeral Mass of her father. Csound was used to process the speech
written and spoken by her siblings. Common Music was used to generate a recitative-like
accompaniment to the processed speech. The composition was assembled using a MIDI
sequencer that supports digital audio. [Simoni, 1997]
To configure Common Music so that it works with the OMS MIDI Driver, include a line of code
in the cminit.lisp file that establishes the MIDI connection. The code takes the general form:
where input-device and output-device are the MIDI device names in your OMS Setup.
Example A.1 is a sample line of code included in cminit.lisp that establishes MIDI
communication with aRoland JV-80, Roland Sound Canvas, Akai S2000, and a Zeta VC-225.
The MIDI input and output device names are consistent with the device names in the OMS
Setup as seen in Figure A.1.
Example A.1
(setf *midi-default-connections*
'(("JV-80" "JV-80")
("Sound Canvas" "Sound Canvas")
("S2000" "S2000")
("Zeta VC-225" "Zeta VC-225")))
After launching Common Music, use the Common Music command open midi to open a MIDI
connection. Common Music responds with the following message:
To establish the Common Music syntax as MIDI, use the Common Music function IN-SYNTAX either at the
Common Music or Stella prompt, in cminit.lisp, or in your Common Music source code.
In order to implement the Csound score file output, define an object named i1 using the
Common Music macro DEFOBJECT. The new object is a subclass of csound-note. A standard
class i1 is created that corresponds to the i1 statement in the Csound scorefile. Additional slots
for object i1 are dur, freq, and amp. Parameters for the I-statement are instrument number (ins),
start time or rhythm (time ), duration (dur ), amplitude (amp ), and frequency (freq ). These
parameters correspond to the parameter fields p1, p2, p3, p4, and p5 in the Csound scorefile.
Example B.1
(in-package :cm)
(defobject i1 (csound-note)
(dur freq amp)
(:parameters ins time dur amp freq))
#<Package "COMMON-MUSIC">
#<STANDARD-CLASS I1>
Now that Common Music knows the current syntax is Csound and the parameter fields for an i1
object, we program a generator to output i1 statements:
Example B.2
(generator csound-test i1 (length 20 dur .1 rhythm .1)
(setf freq (between 220 440))
(setf amp (between 20000 32000)))
#<GENERATOR: Csound-Test>
Open a stream to a file so Common Music can output the results of the generator csound-test.
The Common Music OPEN command opens a stream to the file named csound.sco. The
Common Music MIX command calculates the slot values and writes the result to csound.sco. We
do not play the file since we will use csound.sco as input to the Csound compiler.
s
i1 0.0 0.1 27292 313
i1 0.1 0.1 23518 417
i1 0.2 0.1 28220 336
i1 0.3 0.1 26393 424
i1 0.4 0.1 29430 373
i1 0.5 0.1 22509 249
i1 0.6 0.1 26593 283
i1 0.699 0.1 28623
238
i1 0.799 0.1 21379 380
i1 0.899 0.1 27927
308
i1 0.999 0.1 26589
380
i1 1.099 0.1 24515 335
i1 1.2 0.1 26218 258
i1 1.3 0.1 25070 391
i1 1.4 0.1 26345 274
i1 1.5 0.1 28330 267
i1 1.6 0.1 25479 268
i1 1.7 0.1 23905 349
i1 1.8 0.1 28771 244
i1 1.9 0.1 28062 335
e
You may add additional parameter fields by adding slots to the parameter list and specifying the
order in which the values should be output to the parameter list. Example B.3 extends Example
B.2 by adding a parameter field for pan.
Example B.3
(in-package :cm)
(defobject i1 (csound-note)
(dur freq amp pan)
(:parameters ins time dur amp freq pan))
#| COMMENT |#
/=
; COMMENT
'
<
<=
>
>=
ABORT
ABS
ABS
AND
APPEND
APROPOS
AREF
ASSOC
ATOM
BUTLAST
CAR
CASE
CDR
COND
CONS
CONSP
COUNT-IF
DECF
DEFUN
DO
DO*
DOCUMENTATION
DOLIST
DOTIMES
ENDP
ENDP
EQUAL
EVENP
EVERY
EXPT
FIND-IF
FIRST
FLOAT
FLOAT
FLOATP
FORMAT
IF
INCF
INTEGERP
INTERSECTION
LAMBDA
LAST
LENGTH
LET
LET*
LIST
LISTP
LOOP
MAKE-ARRAY
MAPCAR
MEMBER
MINUSP
MOD
NIL
NTH
NULL
NUMBERP
NUMBERP
ODDP
OR
PLUSP
QUOTE
RANDOM
READ
REMOVE-IF-NOT
REST
RETURN
REVERSE
REVERSE
ROUND
SECOND
SET-DIFFERENCE
SETF
SQRT
SUBSETP
SYMBOLP
SYMBOLP
THIRD
UNION
UNLESS
WHEN
WITH-OPEN-FILE
ZEROP
*cm-state* (variable)
*standard-scale* (variable)
*standard-tempo* (variable)
? (command)
algorithm (container)
amplitude (slot)
archive (command)
between (function)
channel (slot)
close (command)
container (object)
count (variable)
defobject (macro)
delete (command)
duplicate (command)
duration (slot)
expunge (command)
generator (container)
go (command)
heap (container)
help (command)
import (command)
increment (command)
in-syntax (function)
interp (function)
invert (command)
item (function)
list (command)
load (command)
map (command)
markov-analyze (function)
merge (container)
midi-note (object)
mix (command)
mute (container)
new (command)
note (object)
note (slot)
object (function)
open (command)
read-items (function)
retrograde (command)
rhythm (slot)
scale (command)
set (command)
shuffle (command)
sprout (function)
status? (function)
tempo (macro)
thread (container)
time (variable)
transpose (command)
unless-chording (macro)
unless-ending (macro)
unless-resting (macro)
up (command)
vars (declaration)
when-chording (macro)
when-ending (macro)
when-resting (macro)
Glossary
1/f noise - a class of noise whose values correlate logarithmically with past values.
Algorithmic Composition - the use of computers to implement procedures that result in the
generation of music.
Array - a data type that stores information in indexed locations. Arrays may store data in
multiple dimensions.
Atom - any LISP object that is not a cons cell. NIL is both an atom and a list.
Brownian motion or 1/f 2 noise - Brownian motion is the observed movement of small
particles randomly bombarded by the molecules of the surrounding medium. Brownian motion
is also referred to as 1/f 2 noise.
Class - a data type that supports inheritance for slots, slot defaults, and methods.
Cons Cell - a portion of computer memory comprised of two parts: the CAR and the CDR. Lists
are made up of cons cells.
Data - information.
Data Type - a classification of data. LISP supports many data types including integers, floating
point values, ratios, symbols, strings, and lists.
Empty List - a list with no elements or the empty list. May be represented as () or NIL.
Flat List - a list that does not contain any lists as elements.
Frozen Generator - a generator whose elements have been computed once and will remain
fixed until the generator is unfrozen and re-computed.
Information clause - a phrase used in conjunction with the Common Music map command to
get information about an object or objects.
Inheritance Link - a connection between classes that enables information about slots and
methods to flow hierarchically from description of classes to description of individuals.
Isorhythm - literally means "same rhythm." The compositional practice of mapping a rhythmic
pattern onto a pitch sequence.
Item stream accessor - retrieves one value at a time from the item stream.
Item stream data types - the kinds of information that may be assigned to slots. Item stream
data types end in the letter "s." For example, amplitudes, notes, and rhythms are item stream
data types.
Item stream macros - a function used in conjunction with item streams to elicit a particular
behavior. Examples of Item Stream Macros include chord, crescendo, and diminuendo.
Item stream pattern type - the ordering of objects in an item stream. Examples of item
stream pattern types include cycle, heap, palindrome, and random.
Keyword argument - a symbol that begins with a colon that modifies the behavior of a
procedure.
LISP - a programming language that derives its name from List Processing.
Local variable - a variable that exists in a local lexical context such as a LET or LET*.
Mapping - the process of transforming a range of values onto another range of values.
Markov Process - a probability system where the likelihood that an event will be selected is
based on one or more past events. A matrix referred to as a transition table is used to show the
probability that a certain event will be selected based on one or more past events. The order of
the transition table is dependent on the number of past events considered in the selection of the
next event. A first-order transition table describes the probability that an event will be selected
given one past event and a second-order transition table describes the probability that an event
will be selected given two past events. The succession from one event to the other is called a
markov chain.
Octatonic Scale - a series of eight pitches that alternate whole and half steps for one octave.
Palindrome - a pattern that reads the same forwards as it does backwards.
Pitch Class - an integer in the range 0-11 that represents a symbolic note name.
Pitch Class Set (pc set) - a collection of integers representing pitch classes.
Prime Form - the arrangement of pitch classes such that the smallest intervals are at the
beginning of the set, the interval between the first and last members of the set is smaller than
the interval between the last and first members of the set, and the first pitch class is 0.
Probability - the study of outcomes that produce a given event to the total number of possible
outcomes. A probability is the likelihood that a particular event will occur. A probability is
expressed as a ratio- the likelihood that a given event will occur in relation to the total number
of possible outcomes.
Stochastic Music - the use of probability theory in the selection of musical parameters.
Truth Table - a table representation that shows a T or NIL evaluation when two or more
expressions are combined by a logical operator.
Variable - a place in memory where data is stored. Common LISP and Common Music
variables are named by symbols.
Well-Formed List - a list that has an equal number of left and right parentheses.
White noise or 1/f 0 noise - a class of noise that results when all events have an equal
probability of being selected. White noise has a flat spectrum meaning that its energy is
dispersed evenly throughout a specified range. White noise is also referred to as 1/f 0 noise
because of its flat spectrum.
Bibliography
Adorno, Theodor. 1980. Philosophy of Modern Music . New York, NY: The Seabury Press.
Aiken, Jim. 1996. The Limitations of EMI. Computer Music Journal 20 (3):5-7.
Ames. 1989. The Markov Process as a Compositional Model: A Survey and Tutorial. Leonardo 22
(2):175-187.
Apel, Willi. 1979. Harvard Dictionary of Music . Cambridge, MA: The Belknap Press of Harvard
University Press.
Bach, J.S. 1752. Die Kunst der Fuge . Edited by Czerny. New York, NY: C.F. Peters Corporation.
Berg, Paul. 1996. Abstracting the Future: The Search for Musical Constructs. Computer Music
Journal 20 (3):24-27.
Boulanger, Richard Charles. 1999. The Csound Book: Perspectives in Software Synthesis, Sound
Design, Signal Processing,and Programming . Cambridge, MA: The MIT Press.
Boynton, L., P. Lavoie, Y. Orlarney, C. Rueda, and D. Wessel. 1986. MIDI-Lisp, a Lisp-based
music programming environment for the Macintosh. Proceedings of the 1986 International
Computer Music Conference. San Franciso, CA: The International Computer Music Association.
Brindle, Reginald Smith. 1969. Serial Composition . London, England: Oxford University Press.
Brooks, Stephen and Brian J. Ross. 1996. Automated Composition from Computer Models of
Biological Behavior. Leonardo Music Journal 6:27-31.
Bukofzer, Manfred F. 1947. Music in the Baroque Era . New York, NY: W. W. Norton &
Company, Inc.
Chadabe, Joel. 1997. Electric Sound: The Past and Promise of Electronic Music . Upper Saddle
River, NJ: Prentice-Hall, Inc.
Cope, David. 1991. Computers and Musical Style . Edited by J. Strawn. 6 vols. Vol. 6, The
Computer Music and Digital Audio Series . Madison, WI: A-R Editions.
Dannenberg, Roger. 1989. The Canon Score Language. Computer Music Journal 13 (1):47-56.
Dewdney, A.K. 1985. Computer Recreations. Scientific American , August, 1985, 16-24.
Dodge, Charles and Curtis Bahn. 1986. Musical Fractals. Byte 11 (6):185-196.
Dodge, Charles. 1988. Profile: A Musical Fractal. Computer Music Journal 12 (3):10-14.
Dodge, Charles and Jerse, Thomas. 1997. Computer Music: Synthesis, Composition, and
Performance . 2nd ed. New York, NY: Schirmer Books.
Ebciouglu, K. 1992. An Expert System for Harmonizing Chorales in the Style of J.S. Bach. In
Understanding Music with AI . Menlo Park, CA: AAAI Press.
Ernst, David. 1977. The Evolution of Electronic Music . New York, NY: Schirmer Books.
Forte, Allen. 1973. The Structure of Atonal Music . New Haven, CT: Yale University Press.
Grout, Donald Jay. 1973. A History of Western Music . Revised ed. New York, NY: W. W. Norton
& Company, Inc.
Hiller, Lejaren and Isaacson, Leonard. 1959. Experimental Music . New York, NY: McGraw-Hill,
Inc.
Hiller, Lejaren. 1970. Music Composed with Computers-A Historical Survey. In The Computer
and Music , edited by H. Lincoln. Ithaca, NY: Cornell University Press.
Hoppin, Richard H. 1978. Medieval Music . New York, NY: W. W. Norton and Company.
International MIDI Association. 1983. MIDI Musical Instrument Digital Interface Specification
1.0 . Los Angeles, CA: International MIDI Association.
Kao, Edward P.C. 1997. An Introduction to Stochastic Processes . Belmont, CA: Wadsworth
Publishing Company.
Knuth, Donald E. 1973. The Art of Computer Programming, Vol. 1: Fundamental Algorithms .
Reading, MA: Addison-Wesley, Inc.
Lansky, Paul. 1990. Cmix. Princeton, NJ: Godfrey Winham Laboratory, Princeton University.
Loy, Gareth. 1989. Composing with Computers-A Survey of Some Compositional Formalisms
and Music Programming Languages. In Current Directions in Computer Music Research ,
edited by M. M. a. J. Pierce. Cambridge, MA: The MIT Press.
Machlis, Joseph. 1961. Introduction to Contemporary Music . 2 ed. New York, NY: W. W.
Norton & Company.
Mandelbrojt, J. Fremoit, and R. Malina, editors. 1999. The Aesthetic Status of Technological Art.
Leonardo 32 (3):211-215.
McAlpine, K., E. Miranda, S. Hoggar. 1999. Making Music with Algorithms: A Case Study
System. Computer Music Journal 23 (2):19-30.
Minsky, Marvin. 1981. Music, Mind and Meaning. In Music, Mind and Brain: The
Neuropsychology of Music , edited by M. Clynes. New York, NY: Plenum.
Moore, F. Richard. 1990. Elements of Computer Music . Englewood Cliffs, NJ: Prentice-Hall.
Rahn, J. 1990. The Lisp kernel: a portable software environment for composition. Computer
Music Journal 14 (4):42-58.
Roads, Curtis. 1996. The Computer Music Tutorial . Cambridge, MA: The MIT Press.
Rodet, X and P. Cointe. 1984. FORMES: composition and scheduling of processes. Computer
Music Journal 8 (3):32-50.
Rothstein, Joseph. 1992. MIDI: A Comprehensive Introduction . 2nd ed. Madison, WI: A-R
Editions, Inc.
Rowe, Robert. 1994. Interactive Music Systems . Cambridge, MA: The MIT Press.
Salzman, Eric. 1974. Twentieth-Century Music: An Introduction . 2nd ed, Prentice Hall History
of Music Series . Englewood Cliffs, NJ: Prentice-Hall, Inc.
Schottstaedt, William. 1991. Common Lisp Music. Palo Alto, CA: Center for Computer Research
in Music and Acoustics, Stanford University.
Seay, Albert. 1975. Music in the Medieval World . Edited by H. W. Hitchcock. 2nd ed, Prentice-
Hall History of Music Series . Englewood Cliffs, NJ: Prentice-Hall, Inc.
Simoni, M., B. Broening, C. Rozell, C. Meek, G.Wakefield. 1999. A Theoretical Framework for
Electro-Acoustic Music. Proceedings of the 1999 International Computer Music Conference. San
Francisco, CA: International Computer Music Association.
Spiegel, Laurie. 1996. That was Then-This is Now. Computer Music Journal 20 (1):42-45.
Steele, Guy L. 1990. Common LISP - The Language . 2 ed. Bedford, MA: Digital Press.
Taube, Heinrich. 1991. Common Music: A Music Composition Language in Common Lisp and
CLOS. Computer Music Journal 15 (2):21-32.
Taube, Heinrich. 1993. Stella: Persistent Score Representation and Score Editing in Common
Music. Computer Music Journal 17 (4):38-50.
Taube, Heinrich. 1995. An Object-Oriented Representation for Musical Pattern Definition. New
Music Research 24 (2):121-129.
Taube, Heinrich. 1997. An Introduction to Common Music. Computer Music Journal 21 (1):29-
34.
Taube, Heinrich. 2000. Common Music [WWW Site]. The Center for Computer Research in
Music and Acoustics, 2000 [cited May 18, 2000]. Available from http://ccrma-
www.stanford.edu/CCRMA/Software/cm/cm.html.
Voss, Richard F. and John Clarke. 1978. "1/f Noise in Music: Music from 1/f Noise". Journal of
the Acoustical Society of America 63 (1):258.
Winston, Patrick Henry and Horn, Berthold Klaus Paul. 1989. LISP . 3rd ed. Reading, MA:
Addison-Wesley Publishing Company.
Xenakis, Iannis. 1971. Formalized Music . Bloomington, IN: Indiana University Press.
Xenakis, Iannis. 1992. Formalized Music . Revised ed. New York, NY: Pendragon Press.
Discography
Bach, J.S. Musical Offering . LL 1181 London.
de Vitry, Phillipe. Garrit gallus - In nova fert . 77095-2-RC Deutsche Harmonia Mundi.
Degazio, Bruno. On Growth and Form . San Francisco, CA: ICMC92: The International
Computer Music Association.
Dobrian, Christopher. Entropy for computer-controlled piano. Artful Devices, Music for Piano
and Computers, EMF 2000.
Furman, Pablo. Matices Coincidentes . Los Angeles, CA: The Society of Electro-Acoustic Music
in the United States.
Haas, Jeffrey. Keyed Up, mvmt. III. Los Angeles, CA: The Society for Electro-Acoustic Music in
the United States.
Kimura, Mari. "U" (The Cormorant). San Francisco, CA: ICMC92: The International Computer
Music Association.
Lansky, Paul. Late August . San Francisco, CA: New Albion Records.
Simoni. 1998. Emma Speaks. San Francisco, CA: The International Computer Music
Association. CD-ROM.
Taube, Heinrich. Gloriette for John Cage. San Francisco, CA: ICMC94: The International
Computer Music Association.
Xenakis, Iannis. Strategie, Jeu pour deux orchestres. VCD 47253 Varese Sarabande.
Audio Files
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/1-over-f-music.aif] 1-over-f-music.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/1-over-f-music.mid] 1-over-f-music.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/applicative.aif] applicative.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/applicative.mid] applicative.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/brownian-music.aif] brownian-music.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/brownian-music.mid] brownian-
music.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/case.aif] case.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/case.mid] case.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/cond.aif] cond.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/cond.mid] cond.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/encapsulation.aif] encapsulation.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/encapsulation.mid] encapsulation.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/graph.aif] graph.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/graph.mid] graph.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/harmonize.aif] harmonize.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/harmonize.mid] harmonize.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/mapcar.aif] mapcar.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/mapcar.mid] mapcar.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/member.aif] member.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/member.mid] member.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/more-arrays.aif] more-arrays.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/more-arrays.mid] more-arrays.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/my-first-merge.aif] my-first-merge.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/my-first-merge.mid] my-first-merge.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/nth.aif] nth.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/nth.mid] nth.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/recursion-etude.aif] recursion-etude.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/recursion-etude.mid] recursion-etude.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/reverse.aif] reverse.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/reverse.mid] reverse.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sets.aif] sets.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sets.mid] sets.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sierpinski.aif] sierpinski.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sierpinski.mid] sierpinski.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/simple-example.aif] simple-example.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/simple-example.mid] simple-example.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table-with-sets.aif] table-with-sets.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table-with-sets.mid] table-with-sets.mid
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table.aif] table.aif
[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table.mid] table.mid