You are on page 1of 159

Algorithmic

Composition: A Gentle Introduction to Music Composition Using Common LISP and


Common Music
Mary Simoni

DOI: http://dx.doi.org/10.3998/spobooks.bbv9810.0001.001 [http://dx.doi.org/10.3998/spobooks.bbv9810.0001.001]

Title Page
Title // Algorithmic composition: a gentle introduction to music composition using common
LISP and common music

Author // Mary Simoni

Series // SPO Scholarly Monograph Series

Publisher // The Scholarly Publishing Office, The University of Michigan, University Library

Place of Publication // Ann Arbor, Michigan

Date of Publication // 2003

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.

Engineers - These readers have significant experience in designing and implementing


algorithms but not necessarily for music. They have some knowledge of tonal music theory but
have not likely had formal instruction in music performance or composition.

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.

1.1 Common LISP


The programming language LISP derives its name from List Processing [Winston, 1989].
Common LISP was developed in 1956 by artificial intelligence researchers Allen Newell, J.C.
Shaw, and Herbert Simon [Touretzky, 1990]. Since the early days of LISP, researchers have
discovered the power of LISP's processing capabilities for music. Music, a time-based art form,
oftentimes is conceived as a succession of events. The events may be a series of pitches,
articulation patterns, or a succession of rhythms. Figure 1.1.1 depicts a pitch series that is
accompanied by a list representation of that series. Each item in the list representation is called
an element.

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.

1.2 Common Music


Common Music is an extensible programming environment for computer-based composition.
The development of Common Music began in 1989 by Heinrich Taube [Taube, 1989]. After
several years of continuous evolution, Common Music was awarded first prize in the computer-
assisted composition category at the 1er Concours International de Logiciels Musicaux in
Bourges, France.

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.

Figure 1.2.1: 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.

1.3 Algorithmic Composition


An algorithm is defined as a set of rules or a sequence of operations designed to accomplish
some task or solve a problem. Human beings are very good at designing and implementing
algorithms. From getting dressed in the morning to cooking dinner, we are continuously
developing algorithms to solve life's everyday problems.

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.

1.4 Additional References


Although this book provides you with the fundamentals of algorithmic composition using
Common LISP and Common Music, you may wish to augment your reading with additional
references. Two excellent Common LISP references include, "LISP" by Patrick Henry Winston
and Bertold Klaus Paul Horn [Winston, 1989] and "Common LISP - The Language" by Guy L.
Steele [Steele, 1990]. Common Music is accompanied by voluminous electronic documentation,
much of it in HTML. The Common Music Dictionary , available on the accompanying CD and
the Common Music FTP site [Taube, 2000], is a quick reference covering most aspects of
Com m on Music. For detailed information on MIDI, consult the MIDI Specification and
supporting documents published by the International MIDI Association [IMA, 1983] or a
textbook on MIDI such as "MIDI: A Comprehensive Introduction" by Joseph Rothstein
[Rothstein, 1992]. For additional information on strategies for algorithmic composition, refer to
Chapter 19 of "The Computer Music Tutorial" by Curtis Roads [Roads, 1996] or Chapter 11 of
"Computer Music: Synthesis, Composition, and Performance" by Charles Dodge and Thomas
Jerse [Dodge, 1997].

Chapter 2: The History and Philosophy of


Algorithmic Composition
In Chapter 1, we defined algorithmic composition as the use of a rule or procedure to put
together a piece of music. This chapter will give you a broader understanding of algorithmic
composition, how algorithms have been used throughout music history, and an introduction to
the aesthetic issues of algorithmic composition.

2.1 The Process of Algorithmic Composition


A very simple example of using a procedure to generate a piece of music is to use a 12-sided die
(numbered 1-12) to determine the order of pitches in a composition. An association, or mapping
is made to correlate each pitch in a twelve-tone equal tempered scale with each number on the
die. Figure 2.1.1 demonstrates the mapping of pitches to numbers.
Figure 2.1.1

[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.

2.2 A Brief History of Algorithmic Processes Applied to


Music Composition
The Greek philosopher, mathematician, and music theorist Pythagoras (ca. 500 B.C.)
documented the relationship between music and mathematics that laid the foundation for our
modern study of music theory and acoustics. The Greeks believed that the understanding of
numbers was key to understanding the universe. Their educational system, the quadrivium, was
based on the study music, arithmetic, geometry, and astronomy. Although we have numerous
treatises on music theory dating from Greek antiquity, the Greeks left no clues if mathematical
procedures were applied to the composition of music.

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.

Figure 2.2.1: Talea of the isorhythmic 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]

The next figure shows the color of the same motet.

Figure 2.2.2: Color of the isorhythmic motet De bon espoir-Puisque la douce-Speravi by


Guillaume de Machaut.

[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.

Figure 2.2.3: The tenor of De bon espoir-Puisque la douce-Speravi by Guillaume de Machaut

[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?

2.3 Aesthetics of Algorithmic Composition


Aesthetics is a branch of philosophy that describes the theories and forms of beauty in the fine
arts. It is our unique set of experiences, and our perception of those experiences, that shape our
personal aesthetic. Our personal aesthetic is integrally intertwined with our personality as an
artist. Each work of art is some manifestation of the artist's aesthetic.
In Chapter 1, we discussed Knuth's principles for determining the aesthetic merit of an
algorithm. How do we decide if a composition that uses algorithms has aesthetic merit?

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.

2.4 Suggested Listening


Garrit gallus - In nova fert by Phillipe de Vitry
Musical Offering and The Art of the Fugue by J.S. Bach

Musikalisches Würfelspiel by Wolfgang Amadeus Mozart

Lyric Suite by Alban Berg

Klavierstück XI by Karlheinz Stockhausen

The ILLIAC Suite by Lejaren Hiller and Leonard Isaacson

Amorsima-Morsima and Strategie, Jeu pour deux orchestres by Iannis Xenakis

HPSCHD by John Cage

Chapter 3: Introduction to Common LISP


Chapter 3 introduces you to the fundamentals of Common LISP and programming in LISP. You
will learn about some of the various data types that Common LISP supports and many of its
built-in functions called primitives . You will learn how to write your own functions and become
familiar with typical error messages.

3.1 Data and Functions


Data is information. Common LISP supports several data types including numbers, symbols,
and lists.

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.

3.2 LISP Primitives


Functions operate on data. Functions allow us to process data to achieve a result. Common LISP
functions may be categorized as either primitives or user-defined functions. You may think of
functions as procedures that operate on data. Evaluating an expression is referred to as
evaluating a form. A form is simply some Common LISP expression.

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

The primitive FLOAT returns a floating-point number .

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

The primitive ABS returns the absolute value of its argument.

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

? (length '(c-major (c e g) d-major (d f-sharp a)))


4

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

? (first '(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))

? (rest '(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.

The FIRST and REST of NIL is defined as NIL.

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)

? (third '(c-major (c e g) d-major (d f-sharp a)))


D-MAJOR

The primitive LAST returns the last element of a list as a list.

Example 3.2.12
? (last '(c e g))
(G)

? (last '(c-major (c e g) d-major (d f-sharp a)))


((D F-SHARP A))

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)

? (butlast '(c e g b-flat))


(C E G)

? (butlast '(c-major (c e g) d-major (d f-sharp a)))


(C-MAJOR (C E G) D-MAJOR)

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)

? (reverse '(c-major (c e g) d-major (d f-sharp a)))


((D F-SHARP A) D-MAJOR (C E G) C-MAJOR)

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).

CONS may be used to create lists by CONS ing a symbol to NIL.

Example 3.2.16
? (cons 'c-flat nil)
(C-FLAT)
? (cons '(c e g) nil)
((C E G))

? (cons nil nil)


(NIL)

LIST creates lists from symbols and/or lists by simply adding a level of parentheses to its
inputs.

Example 3.2.17

? (list '(c-major (c e g) d-major (d f-sharp a)))((C-MAJOR (C E G) D-MAJOR (D F-SHARP A)))

? (list 'a '(d e-flat))


(A (D E-FLAT))

? (list 'c-flat nil)


(C-FLAT NIL)

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

? (append '(c e g) 'b-flat)


(C E G . B-FLAT)

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)

? (last (append '(c e g) 'b-flat))


(G . 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]

Example 3.2.21: Compare and contrast CONS, LIST, and APPEND

? (cons '(c e) '(g b-flat))


((C E) G B-FLAT)

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000018.jpg]

? (list '(c e) '(g b-flat))


((C E) (G B-FLAT))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000019.jpg]

? (append '(c e) '(g b-flat))


(C E G B-FLAT)

[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

3.3 LISP Predicates


Predicates are primitives that return a true or false evaluation. True in LISP is represented by
the symbol T and false in LISP is represented by the symbol NIL.

An example of a simple predicate is SYMBOLP.SYMBOLP returns T if the data passed to the


function is a symbol and NIL if it is not.

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

? (equal "this is a string" "this is a string")


T

? (equal '((a b) c) '((a b) c))


T

? (equal '(a (b c)) '((a b) c))


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

? (endp '(c e g))


NIL

? (endp '(c . d))


NIL

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 '(c e g))


NIL

? (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 '(c e g))


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 '(c e g))


NIL

? (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

Form 1 Form 2 Evaluation

T T T

T F F

F T F

F F F

or

Form 1 Form 2 Evaluation

T T T

T F T
F T T

F F F

Example 3.3.16
? (and (numberp 'c-major) (symbolp 'd-major))
NIL

? (or (numberp 'c-major) (symbolp 'd-major))


T

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

3.4 User-Defined Functions


To build programs that solve complex problems, it is necessary to write your own
functions.DEFUN defines a named function . User-defined functions may be used just like the
LISP primitives.

The template to define a function is:


(DEFUN FUNCTION-NAME (OPTIONAL-ARGUMENT-LIST)
FUNCTION DEFINITION)

The template to call a function is:


(FUNCTION-NAME OPTIONAL-ARGUMENT-LIST)

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.

Call the function.


? (transpose-midi-note 60 12)
72

We call the function TRANSPOSE-MIDI-NOTE with inputs of 60 12 to transpose key-number 60


up 12 half steps.

? (transpose-midi-note 60 -5)
55

We call the function TRANSPOSE-MIDI-NOTE with inputs of 60 -5 to transpose key-number 60


down 5 half steps.

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

? (defun rangep (keynumber)


(and (<= keynumber 127) (>= keynumber 0) (integerp keynumber)))
RANGEP

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.

We call the function.

? (rangep 128)
1 NIL

? (rangep -23.5)
NIL

? (rangep 64)
T

3.5 Getting help in LISP


It is a good idea to provide a documentation string for each function that you define. The
documentation string is a string that describes the purpose of the function. For example, the
function RANGEP documentation string may say, "A predicate function that determines if its
input is a valid MIDI keynumber." The documentation string is added to the function definition
after DEFUN but before the function definition.

To define a function that uses a documentation string, use the template:

(DEFUN FUNCTION-NAME (OPTIONAL-ARGUMENT-LIST)


"DOCUMENTATION STRING"
FUNCTION DEFINITION)

Example 3.5.1:

? (defun rangep (keynumber)


"A predicate function that determines if its input is a valid MIDI keynumber."
(and (<= keynumber 127) (>= keynumber 0) (integerp keynumber)))

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

? (documentation 'rangep 'function)


"A predicate function that determines if its input is a valid MIDI keynumber."

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

APROPOS returns CONSP and describes CONSP as a function.

3.6 Error Messages


Error messages may be unsettling, but they are very informative when writing your own
programs. Error messages may appear for many reasons, but one of the most common reasons is
you did something Common LISP does not know about or the syntax has been violated.

Following, are some very common errors:


Example 3.6.1: Unbound variable
? (atom b-flat)
> Error: Unbound variable: B-FLAT
> While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT
> Type Command-/ to continue, Command-. to abort.
> If continued: Retry getting the value of B-FLAT.
See the Restarts... menu item for further choices.
1 >

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

Another common error is to pass the wrong data type to a function.

Example 3.6.2: Input is wrong data type


? (zerop '(c e g))
> Error: value (C E G) is not of the expected type NUMBER.
> While executing: ZEROP
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >

? (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.

Example 3.6.3: Passing the wrong number of arguments to a function


? (cons 'a 'b 'c)
> Error: Too many arguments (no opt/rest)
> While executing: CONS
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >

? (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 >

Chapter 4: Object Oriented Programming and


Common Music
Common LISP has been extended to include the Common LISP Object System (CLOS). CLOS is
a set of tools used to facilitate object-oriented programming in Common LISP. Object-oriented
programming is a style of programming where problems are solved through the development
and relationship among objects.

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.

4.1 Object Hierarchy


Many things we encounter in everyday life are organized hierarchically. Consider traditional
musical instruments . We can group musical instruments into three sections: strings , winds
, and percussion . The winds section is comprised of woodwinds and brass . The wind
instruments are grouped as a family because each member of the family produces sound by
blowing air through a tube. Woodwind instruments include the clarinet, flute and oboe. Brass
instruments include the trumpet, French horn, and tuba. Wind instruments may be described as
a class of musical instruments.

In an object-oriented model, the relationship between objects in the hierarchy is described as


either superclass or subclass . A superclass is an object one-level higher in the hierarchy than an
object and a subclass is an object one-level below. Notice in Figure 4 .1 , instruments is a
superclass of winds and trumpet , horn , and tuba are subclasses of brass . The connection
between a superclass and its subclass is called an inheritance link . The subclass inherits
attributes, referred to as slots , from its superclass.

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.

4.2 Common Music Object System


Common Music uses an object class called container to store musical data.container is at the top
of the class hierarchy. Subclasses of container are thread, merge, and algorithm. Figure 4.2.1 is
graphically describes the relationship among container, thread, merge, and algorithm.
Containers have slots for start and end time. Without a specified start, a container begins
playing immediately or at time 0. Without a specified end or length, a container could
theoretically play forever.

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]

A heap is a kind of thread that reshuffles itself every time it is played.


A generator is a kind of thread that computes the value of its elements and may retain these values in a frozen
state for fixed playback. Generators may be unfrozen and reevaluated which may result in different musical data.
A mute is a kind of algorithm that does not output events. It may be used in conjunction with sprout to
dynamically output musical data.

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

Slot Name Relationship to MIDI 1.0 Specification Default Value

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.

channel MIDI channel in the range 0-15 Default is MIDI channel 0.

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.

4.3 Stella: Common Music's Command Interpreter


Stella is Common Music's command interpreter. Stella serves as a common interface for all ports
of Common Music. When you type a command to Stella, you are issuing a command to Common
Music. The relationship between Stella and Common Music is similar to the relationship
between the Macintosh OS and the Finder. For example, to communicate with the Macintosh
OS, you issue commands (albeit through a graphic interface) to the Finder.

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)

<cr> means press enter or carriage return.


Common Music creates an instance of a generator and prompts you for its name and position
within the container hierarchy. In this case, Common Music assigns a default name of
Generator-10 at a position one-level below the Top-Level. You may override the default
container name assigned by Common Music by typing your generator name at the "Name for
new Generator:" prompt. You may view the container in the hierarchy by using the Common
Music command list. The Stella prompt indicates we are at Top-Level looking down one level in
the hierarchy to our newly created generator named Generator-10.

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

Stella [Top-Level]: go generator-10


Focus: Generator-10

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

Stella [Generator-10]: new midi-note


Number of midi-notes to create: (<cr>=*) 1
Slots and values: note 60 amplitude .75 channel 0 duration .25 rhythm
.5
New object position: (<cr>=Generator-10) <cr>

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

Stella [Generator-10]: list

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

Stella [Generator-10]: open midi


Registering with OMS...
Done.
Stream: #<Midi Driver: Open>

To listen to the midi-note, use the Common Music command mix.

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.

Return to the Top-Level by issuing the Common Music command go up or alternatively, go


Top-Level.

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

Stella [Top-Level]: list


Top-Level:
1. #<GENERATOR: Generator-10> (Frozen)

Use the Common Music command go followed by the container name to change the focus object
back to Generator-10.

Example 4.3.8

Stella [Top-Level]: go generator-10


Focus: Generator-10
Type: Generator
Status: Frozen
Objects: 1
Start: unset

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

Stella [Generator-10]: new thread


Name for new Thread: (<cr>=Thread-12)
New object position: (<cr>=Generator-10)

Use the Common Music command list to see the new thread in the hierarchy.

Example 4.3.10

Stella [Generator-10]: list


Generator-10:
1. #<MIDI-NOTE | 60| 0.500| 0.250| 0.750| 0|>
2. #<THREAD: Thread-12>

Change the current focus object to the new thread.


Example 4.3.11

Stella [Generator-10]: go 2
Focus: Thread-12
Type: Thread
Status: Normal
Position: 2 in Generator-10
Objects: 0
Start: unset

Create a new midi-note and assign its slots.

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)

Use list to see the contents of the thread.

Example 4.3.13

Stella [Thread-12]: list


Thread-12:
1. #<MIDI-NOTE | 67| 0.200| 0.400| 0.600| 0|>

Figure 4.3.3 graphically depicts the arrangement of objects in our hierarchy.

[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|>

Stella [Thread-12]:del 1 Stella [Thread-12]:list


Thread-12:
1. #<MIDI-NOTE | 67| 0.200| 0.400| 0.600| 0|> (Deleted)

Stella [Thread-12]: exp 1

Stella [Thread-12]: list


Thread-12 does not have any objects to list.

4.4 Common Music's Global Variables


Global variables are used to assign values to variables that operate on a systemic level. By
convention, global variable names are enclosed in asterisks. For example, the Common Music
global variable *standard-tempo* sets a global metronome marking measured in beats per
minute (bpm). The default value of *standard-tempo* is 60 bpm. You may query the value of a
global variable in the Stella interpreter by preceding the variable name with a comma. The
comma tells Stella you are not issuing a Common Music command.

Example 4.4.1

Stella [Top-Level]: ,*standard-tempo*

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

Stella [Top-Level]: (setf *standard-tempo* (* 2 *standard-tempo*))


120.0

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

Note Value Ordinal Representation Symbolic Representation

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

Stella [Top-Level]: ,*standard-scale*


#<EQUAL-TEMPERED-SCALE Chromatic-Scale>

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.

4.5 Getting Help in Common Music


On-line help is available in Common Music. Common Music's help facility accesses the Common
Music Dictionary . To find some of the help topics available, type the Common Music command
? at the Stella command interpreter. Common Music displays the available help topics.

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.

Stella [Top-Level]: help mix

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

Stella [Top-Level]: help

The Common Music help command opens the Help Window as seen in Figure 4.5.1.

Figure 4.5.1: The Help Window

[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*.

Figure 4.5.2: Getting help on *standard-tempo*


[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000029.jpg]

Common Music is configured to use a HTML viewer that opens the requested page in the
Common Music Dictionary .

Chapter 5: Introduction to Algorithmic Composition


In Chapter 4, we learned how to create instances of objects and store those objects in a container
in the Common Music class hierarchy using the Stella command interpreter. In this chapter, we
will learn how to create containers and assign its slots by describing writing programs. Section
4.3 is helpful when first getting acquainted with the Common Music object system and some of
the more frequently used commands. More powerful use of the system comes from writing
programs.

5.1 Getting Started


Perhaps the easiest way to write a program is to type code into a file. Consult your Common
LISP documentation to learn how to create, edit, save, compile, and load files.

Before we begin entering code into a file, we must learn how Common Music assigns values to
slots. In section 4.4, we learned how to assign values to global variables using SETF. Common
Music also uses SETF to assign values to slots. Example 5.1.1 assigns the MIDI note slot a value
of 60 with an amplitude of .5.

Example 5.1.1

(setf note 60)


(setf amplitude .5)

Example 5.1.2 shows how you can write a program to create a generator and place a note in that
generator.

Example 5.1.2: my-first-generator.lisp

(generator my-first-generator midi-note (length 1)


(setf note 60)
(setf amplitude .5)
(setf rhythm .25)
(setf duration .7)
(setf channel 0))

Note: examples followed by : and a filename as in Example 5.1.2 are included on the
accompanying compact disc.

What does this code say? We create an instance of a generator called my-first-generator and we
fill the generator with midi-note objects. We can initialize the generator's slots in the
parentheses following the instantiation of the generator. In this case, we initialize the
generator's length slot to have a value of one midi-note. Following the container initialization
list, we enter the body of the generator where the additional values are bound to slots using
SETF. Notice that SETF is used with slot name-slot value pairs enclosed in parentheses.my-first-
generator is closed by a concluding right parenthesis that balances with the left parenthesis that
initiated the program.

What are the various container initialization slots? The container object has optional
initialization parameters for start time in seconds. Because of Common Music's object hierarchy,
thread, merge, and algorithm inherit start from container. In addition, algorithm has container
initializations for length, count, and end.length is the number of note or rest events in the
container.count is a Common Music variable that increments its value based on the number of
note or rest events in a container.end specifies the ending time in seconds.heap inherits from
thread so heap has an optional initialization for start time.generator inherits from thread and
algorithm so generator has optional initializations for start, length, count, and end.mute inherits
from algorithm so mute also has optional initializations for start, length, count, and end.

Once the code has been typed into a file, save the file as my-first-generator.lisp. The .lisp
extension will help you recognize the file as Common Music source code.

Next, evaluate the code by selecting all of the text in my-first-generator.lisp, copying it, and then
pasting it into Stella. Press return and Stella will evaluate the program and create my-first-
generator. Alternatively, you may also evaluate the code by loading the file into Common Music

You may listen to the generator by entering the Common Music command mix my-first-
generator at Stella's Top-Level or change the focus object to my-first-generator and enter the
command mix.

The result of the Common Music evaluation may be saved as a Common Music file using the
Common Music command archive command. Notice that in Example 5.1.3, the focus object is
my-first-generator. Using a file extension of .cm identifies the file as a Common Music archive.

Example 5.1.3: my-first-generator.cm


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

Just as with my-first-generator.lisp, you may load my-first-generator.cm and Common Music
will load and evaluate the file.

What's the difference between the Common Music .lisp source file and the Common Music .cm
archived file? Open and view the contents of my-first-generator.cm. You'll see the code that
Common Music generated when it evaluated my-first-generator.lisp.

In addition to saving the .lisp or .cm source, you may wish to save the musical output from my-
first-generator.lisp to a MIDI file (.mid). Saving the output of Common Music as a standard
MIDI file means you can readily combine the power of Common Music with the functionality of
a MIDI sequencer. Type the Common Music command, open <filename>.mid to open a stream
to a MIDI file. Once the stream is open, mix the container. The output of the container is
directed to <filename>.mid. When you're finished writing the file, use the Common Music
command close to close the stream.

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

Stella [My-First-Generator]: close my-first-generator.mid.copy

Stella [My-First-Generator]:

If you're using Common Music on a Macintosh, from the Common Music menu, select
Streams , New Streams , and MIDI file . Common Music will bring up a window with a
default file name in the title bar of the window. You can enter attributes of the .mid file such as
the filename and start time. Use the mix command to route the MIDI output to the named .mid
file. To redirect MIDI output to the port, from the Common Music menu, select Streams ,
and MIDI .

Table 5.1.1 gives an overview of the file formats discussed in Section 5.1.

Table 5.1.1

File Type File Description Command(s) associated with file


Suffix creation

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

Common .cm File contains CLOS program archive


Music code
Archive

MIDI File .mid File contains MIDI data created open


as a result of opening a stream mix
for MIDI output
5.2 Item Streams
In order to do something musically interesting, we certainly need to produce containers that
have more than one note! The easiest way to create containers that have more than one note is to
use item streams . Item streams are an ordering of objects based on pattern types.

Use the following template to assign slots using items streams:


(SETFslot-name (item-stream-accessor (item-stream-data-typeitem-stream-data in item-stream-pattern-
type)))

Example 5.2.1:

(setf note (item (items 60 84 56 in cycle)))

In example 5.2.1, note is the slot-name. We use item as the item-stream-accessor that accesses
one value at a time from the item stream and assigns it to the note slot. We select one of the
item-stream-data-types (Table 5.2.1) using one of the item-stream-pattern-types (Table 5.2.2).
In the case of Example 5.2.1, the item stream data type is items and the item-stream-pattern-
type is cycle. The cycle pattern type circularly selects items reading from left to right for as
many events are required.cycle is the default item-stream-pattern-type. Manipulating patterns
using item streams is a simple yet very powerful approach to algorithmic composition.

Let's consider another example.

Example 5.2.2: item-streams.lisp

(generator item-streams midi-note (length 10 channel 0)


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

(setf duration rhythm))

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

Example 5.2.3: Output from item-streams.lisp

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

Notice that the note slot has been assigned one item from the item stream. The assignment is
cyclic and continues for the ten events specified by the container's length slot. A rest object is
created using the symbol r. The amplitude and rhythm slots are also assigned in cycle by default.
The rest is assigned a rhythmic value of .7 seconds because of the cyclic pattern used to assign
the rhythm slot. The duration slot is dependent on the value of the rhythm slot. Notice that MIDI
channel 0 was assigned in the container initialization list. Slots that do not vary may be assigned
in the container initialization list. Example 5.2.2 also demonstrates the use the semi-colon or #|
|# to make comments in Common LISP and Common Music.

Table 5.2.1 describes the different item stream data types. All of the item stream data types end
in the letter "s." This naming convention helps you distinguish between item stream data types
and item-stream-accessors that do not end in the letter "s."

Table 5.2.1: Item Stream Data Types

Item Description Example on Slot Use


Stream accompanying
Data compact disc
Types

amplitudes Constructs item streams of amplitudes.lisp amplitude slot


symbolic amplitudes in the
range fff - pppp

degrees Constructs item streams of degrees.lisp note slot


symbolic note names or Similar to item stream
data types pitches and
keynumbers notes

intervals Constructs item streams from a intervals.lisp note slot


list of integer intervallic
distances given a specified
starting value

items Constructs items streams of items.lisp note, amplitude, duration,


floating point and/or integer rhythm, or channel slots
values

notes Constructs item streams of notes.lisp note slot


symbolic note names or Similar to item stream data
types degrees and pitches
keynumbers
numbers Constructs item numbers.lisp Note, amplitude, duration, rhythm, or
streams of cyclic channel slots.
Implements the following options:in,
or random from, to, below, downto, above, and by
numbers using
constructor
options

pitches Constructs item pitches.lisp note slot


streams of Similar to item stream data type degrees and notes.

symbolic note
names or
keynumbers

rhythms Constructs item rhythms.lisp rhythm or duration slots


streams of ordinal May be used with the item stream modifier tempo to set
the local tempo of a particular container distinct from the
or symbolic note value of *standard-tempo*.
values

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*

Table 5.2.2 describes many of the item stream pattern types.

Table 5.2.2: Item Stream Pattern Types

Item Description Example on Notes


Stream accompanying
Pattern compact disc
Types

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.

Table 5.2.3: Item Stream Macros


Macro Description Example of Notes
CD

chord Creates a pitch chords.lisp


simultaneity. May
optionally enclose
pitches in [] to
indicate a chord.

crescendo Creates a gradual crescendo.lisp Requires modifier in to specify number


increase in amplitude of events in the crescendo

diminuendo Creates a gradual diminuendo.lisp Requires modifier in to specify number


decrease in amplitude of events in the diminuendo

expr Creates items based expr.lisp


on the value of an
expression

mirror Creates a stream mirror.lisp May be used with an item stream


followed by a pattern type
retrograde of the
stream

repeat Creates a specified repeat.lisp When used in conjunction with length


number of repeats of container initialization.length has
an item stream higher precedence than repeat in
determining number of events.

retrograde Creates a retrograde retrograde.lisp


of an item stream. The
last note of the initial
item stream is
repeated before the
retrograde.

series Creates an item series.lisp Implements options for prime (p),


stream for serial row retrograde (r), inversion (I) and
operations. retrograde inversion (ri). Also
implements options for multiple
(returns product of multiple and
specified pitch class) and modulus
(returns result of modulo operator for
specified pitch class).
5.3 A Complete Example
Example 5.3.1 uses a merge container called my-first-merge saved as my-first-merge.lisp. The
merge container contains 6 generators:generator 1, 2, 3a, 3b, 3c, and 3d. The merge is based
on the octatonic scale , initially presented in series. The octatonic scale is a series of eight
pitches that alternate whole and half steps for one octave. Permutations of the octatonic scale
form the pitch material of the remaining generators as a means of creating pitch homogeneity.

Example 5.3.1: my-first-merge.lisp


#|
Define a function to scale the duration slot.
The function scale-duration is used in generator 1a.
|#
(defun scale-duration (a-number a-percentage)
(* a-number a-percentage))

(merge my-first-merge ()
#|
my-first-merge is the container that performs parallel processing of its generators 1, 2, 3a, 3b, 3c and 3d.
|#

(generator 1a midi-note (length 48 channel 0)


#|
Generator 1 states the fundamental pitch material for the merge, presented melodically in series
|#
(setf note (item (series 0 1 3 4 6 7 9 10 from 'ef3
forming (items p i r ri in random)
returning note)))
(setf rhythm (item (items .22 .23 .25 .26 .28 .29 .31 in accumulation)))
(setf amplitude (item (crescendo from pppp to fff in 48)))
(setf duration (scale-duration rhythm amplitude)))

(generator 2a midi-note (start 7 end 11.5 channel 1)


#|
Generator 2 provides chords that punctuate the melodic material
|#
(setf note (item (notes [c2 d3 ef4 f5] r [cs1 ds2 e3 fs4 gs5] in random)))
(setf rhythm (item (rhythms e e. (s weight .5) s. in random)))
(setf amplitude (item (amplitudes mf f ff in heap)))
(setf duration (/ rhythm 2)))

(generator 3a midi-note (start 2.8 length 4 channel 2 duration .1)


#|
Generators 3a-3d provide an accompaniment to pitch material
|#
(setf note (item (notes c4 d5 ef6 f6)))
(setf amplitude (item (crescendo from mp to mf in 4)))
(setf rhythm (item (items .01 .035 .048 in heap))))
(generator 3b midi-note (start 3.75 length 5 channel 0 duration .1)
(setf note (item (notes cs3 ds4 e5 fs6 gs7)))
(setf amplitude (item (crescendo from mp to mf in 5)))
(setf rhythm (item (items .01 .035 .048 in heap))))
(generator 3c midi-note (start 4.1 length 4 channel 0 duration .1)
(setf note (item (notes c4 d5 ef6 f6)))
(setf amplitude (item (crescendo from mp to mf in 4)))
(setf rhythm (item (items .01 .035 .048 in heap))))
(generator 3d midi-note (start 4.8 length 7 channel 0 duration .1)
(setf note (item (notes [cs2 ds3 e4] r [ds3 e4 fs5] [e4 fs5 g6] [fs5 g6 gs7] [g6 a7 as8])))
(setf amplitude (item (crescendo from ppp to mp in 5)))
(setf rhythm (item (items .12 .124 .126 .128 .132 in heap)))))

Notice the user-defined function SCALE-DURATION in Generator 1.SCALE-DURATION is defined as:


(defun scale-duration (a-number a-percentage)
(* a-number a-percentage))

A user-defined function must be evaluated by the interpreter or compiled and loaded into
Common Music before the function is used in a Common Music container. It is good practice to
include user-defined functions at the beginning of the file that creates containers that reference
the functions.

To listen to the merge, load my-first-merge.lisp into Common Music or simply copy the source
code and paste it into Stella. Stella creates my-first-merge. If you type the Common Music
command list, you will see that not only did Stella create my-first-merge, but the generators have
also been created.

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/my-first-merge.mp3] my-first-merge.mp3

Example 5.3.2

Stella [Top-Level]: list


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

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

Example 5.3.3

Stella [Top-Level]: go 2

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

Return to the first level and mix.

Example 5.3.4

Stella [1a]: up

Focus: My-First-Merge
Type: Merge
Status: Normal
Objects: 6
Start: unset

Stella [My-First-Merge]: mix


Mix objects: (<cr>=My-First-Merge)
Start time offset:(<cr>=None)
; Warning: MIDI already open.
; While executing: MIDI-OPEN

You should hear the output from my-first-merge with the generators entering as specified in
their container initialization lists.
5.4 Suggesting Listening
"U" (The Cormorant) for violin, computer, and quadraphonic sound composed by Mari Kimura
is motivated by the blight of the oil-covered cormorants in the Persian Gulf. The formal
structure of the composition is quasi-palindromic, imitating the shape of the letter "U." [Kimura,
1992]

Chapter 6: The Stella Command Interpreter


In Chapter 5, we discussed some basic commands issued to the Stella command interpreter such
as new, go, archive, and open. The Stella command interpreter provides a number of powerful
commands that allow you to edit slots after computing a container. Editing specific slots is
useful if you like the output from a container, but want to make a few adjustments to the output.
To demonstrate these slot editing commands, we create a generator named slot-editing and
assign its slots using item streams as shown in Example 6.1.

Example 6.1: slot-editing.lisp

(generator slot-editing midi-note (length 5)


(setf note (item (notes c4 cs d in cycle)))
(setf amplitude (item (amplitudes mp mf f in sequence)))
(setf duration (item (rhythms e q h in heap)))
(setf rhythm duration))

6.1 Referencing Slot Data


Common Music lists up to 50 objects in a container using the list command.

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|>

6.2 Assigning Slot Data using set


The set command may be used in conjunction with object referencing to reassign slots. It is easy
to confuse set and SETF. Use SETF to assign slots or global variables. Use the set command to
assign slots after the slot has been assigned.

The set template is:


set <object reference> <slot-name1> <slot-value1>
<slot-name2> <slot-value2> <slot-name-n> <slot-value-
n>

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

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.750| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.750| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.750| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.700| 0|>

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)

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.350| 0|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 0|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 0|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 0|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 0|>

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)

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.350| 0|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 1|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 2|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 1|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 0|>

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

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C4| 0.750| 0.750| 0.350| 1|>
2. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 1|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 2|>
4. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 1|>
5. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 0|>

6.3 Using Other Functions to Reassign Slot Values


Common Music has several commands besides set to assign slot values. Let's apply some of these commands to
the objects created in Section 6.2.
The retrograde command reverses the order of objects. Optionally, you may specify a range. The
retrograde template is:

retrograde <optional range>

Example 6.3.1
Stella [Slot-Editing]: retrograde
Retrograde positions: (<cr>=Slot-Editing)

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 0|>
2. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 1|>
3. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 2|>
4. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 1|>
5. #<MIDI-NOTE | C4| 0.750| 0.750| 0.350| 1|>

Stella [Slot-Editing]: retrograde 1:3

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 2|>
2. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 1|>
3. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 0|>
4. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 1|>
5. #<MIDI-NOTE | C4| 0.750| 0.750| 0.350| 1|>

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))

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | D4| 0.500| 0.500| 0.950| 0|>
2. #<MIDI-NOTE | C4| 2.000| 2.000| 0.350| 1|>
3. #<MIDI-NOTE |CS4| 0.500| 0.500| 0.650| 2|>
4. #<MIDI-NOTE |CS4| 2.000| 2.000| 0.650| 2|>
5. #<MIDI-NOTE | C4| 0.750| 0.750| 0.350| 1|>

The invert command reverses the direction of each interval from a specified note. Optionally,
you may specify a range. The invert template is:

invert < range> <slot> <expression>

Example 6.3.3
Stella [Slot-Editing]: invert * note
ef5

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | E6| 0.500| 0.500| 0.950| 0|>
2. #<MIDI-NOTE |FS6| 2.000| 2.000| 0.350| 1|>
3. #<MIDI-NOTE | F6| 0.500| 0.500| 0.650| 2|>
4. #<MIDI-NOTE | F6| 2.000| 2.000| 0.650| 2|>
5. #<MIDI-NOTE |FS6| 0.750| 0.750| 0.350| 1|>

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.

T h e increment command increases or decreases a specified slot by a specified value or


expression. The increment template is:

increment <range> <slot> <expression>

In the following example, we subtract .2 seconds from the value of all of the rhythm slots.

Example 6.3.4
Stella [Slot-Editing]: increment * rhythm
-.2

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | E6| 0.300| 0.500| 0.950| 0|>
2. #<MIDI-NOTE |FS6| 1.800| 2.000| 0.350| 1|>
3. #<MIDI-NOTE | F6| 0.300| 0.500| 0.650| 2|>
4. #<MIDI-NOTE | F6| 1.800| 2.000| 0.650| 2|>
5. #<MIDI-NOTE |FS6| 0.550| 0.750| 0.350| 1|>

The transpose command transposes the note slot a specified interval measures in half steps. The
transpose template is:

transpose <range> <slot> <expression>

The following example transposes all notes down one octave.

Example 6.3.5
Stella [Slot-Editing]: transpose * note
-12

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | E5| 0.300| 0.500| 0.950| 0|>
2. #<MIDI-NOTE |FS5| 1.800| 2.000| 0.350| 1|>
3. #<MIDI-NOTE | F5| 0.300| 0.500| 0.650| 2|>
4. #<MIDI-NOTE | F5| 1.800| 2.000| 0.650| 2|>
5. #<MIDI-NOTE |FS5| 0.550| 0.750| 0.350| 1|>

The shuffle command randomly reorders objects. The shuffle template is:

shuffle <optional range>

Example 6.3.6
Stella [Slot-Editing]: shuffle 1:3

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | E5| 0.300| 0.500| 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|>

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

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|>

6.3 Scale Predicates


Common Music has predicate functions that test the value of the note slot in relation to a
reference. These predicate functions all begin with scale and have appended to them a relational
operator as shown in Table 6.3.1. The reference may be a keynumber, frequency, or symbolic
note name. Symbolic note names must be quoted.

Note that these scale predicates are different from the scale command as described in Example
6.3.7. Scale predicates return a T or NIL value whereas the scale command alters the value of a
slot by a specified percentage.

Table 6.3.1: Scale Predicates

Predicate Example Explanation

scale= reference (scale= 'fs5) Returns T if scale reference is f-sharp 5

scale/= (scale/= 'fs5) Returns T if scale reference is not f-sharp 5


reference

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<= (scale<= Returns T if scale reference is less than or equal to f-sharp 5


reference 'fs5)

scale>= (scale>= Returns T if scale reference is greater than or equal to f-sharp


reference 'fs5) 5

Scale predicates may be used in conjunction with the map command as seen in Section 6.4 or
with conditionals as discussed in Chapter 9.
6.4 Mapping
The map command is a powerful way to evaluate slot data based on a clause. A clause may be a
way to gather information about slots or a condition applied to slots. To apply a clause to a range
of slots, specify the objects to be mapped, and then the clause to be applied to those objects. The
map command maps the clause to the specified slots. The map template is:

map <objects> <clause>

For <objects>,map uses object referencing as discussed in Section 6.1. The power of the map
command lies in the <clause>. One type of clause that map uses is the information clause .
Information clauses return information about the mapped objects.

Table 6.4.1 describes map' s information clauses and gives an example of its use. The returned
value is based on the following midi-notes in the container named slot-editing:

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

Information Description Example Evaluation


Clause

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

average Calculates the average for a specified number of map * CLAUSE


objects for a given slot. Does not work with average COUNT VALUE
symbolic note names duration average duration
5 1.1

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]

analyze Performs a statistical analysis on slot for a specified map * CLAUSE


range. Analysis includes number of unique values, analyze COUNT VALUE
minimum, maximum, mean, variance, deviation rhythm analyze rhythm
and a breakdown.
5

Unique:3
Minimum: 0.300
Maximum: 1.800
Mean: 0.950
Variance: 0.613
Deviation: 0.783
map may also use Common LISP conditionals such as WHEN and UNLESS. First, let's discuss
WHEN and UNLESS before resuming our discussion of the map command and conditional clauses.

The Common LISP macros WHEN and UNLESS use the following templates:

(WHEN <CONDITION> <CONSEQUENT-CLAUSE>)

(UNLESS <CONDITION> <CONSEQUENT-CLAUSE>)

With WHEN, if <CONDITION> evaluates to T, LISP evaluates the <CONSEQUENT-CLAUSE>).


With UNLESS, if <CONDITION> evaluates to T, LISP does not evaluate the <CONSEQUENT-
CLAUSE>).WHEN and UNLESS are logical complements.

Examples 6.4.2 and 6.4.3 reference the value of *standard-tempo* which has a value of 60.

Stella [Slot-Editing]: ,*standard-tempo*


60.0

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.

Recall our slot data from Example 6.4.1.

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|>

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

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C4| 0.300| 0.250| 0.950| 0|>
2. #<MIDI-NOTE | C4| 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|>

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

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C3| 0.300| 0.250| 0.950| 0|>
2. #<MIDI-NOTE | C4| 0.300| 0.500| 0.650| 2|>
3. #<MIDI-NOTE |FS4| 1.800| 2.000| 0.350| 1|>
4. #<MIDI-NOTE | F5| 1.800| 2.000| 0.650| 2|>
5. #<MIDI-NOTE |FS4| 0.550| 0.750| 0.350| 1|>

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

Stella [Slot-Editing]: list


Slot-Editing:
1. #<MIDI-NOTE | C4| 0.300| 0.250| 0.950| 3|>
2. #<MIDI-NOTE | C4| 0.300| 0.500| 0.650| 3|>
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|>

6.5 Duplicating A Container

Common Music commands such as set, retrograde, invert, increment, transpose and shuffle are
destructive operations. Once these commands are issued, there is no way to "un do" what has
been done. For this reason, it is a good idea to make a copy of a container and its contents before
issuing these commands.

To copy a container and its contents, use the Common Music command duplicate.

Example 6.5.1

Stella [Slot-Editing]: duplicate


Duplicate object: slot-editing
Pasted Slot-Editing-Copy to Top-Level.
Stella [Slot-Editing]: up

Focus: Top-Level
Type: Container
Status: System
Objects: 2

Stella [Top-Level]: list


Top-Level:
1. #<GENERATOR: Slot-Editing>

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.

Chapter 7: Printing and Reading


This chapter introduces you to writing output to your computer's monitor and reading data from
the computer keyboard. Displaying information to your monitor is very helpful in locating
problems in your programs.

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

Stella [Top-Level]: (print 'a-symbol)


Stella [Top-Level]:
A-SYMBOL
A-SYMBOL

Stella [Top-Level]: (print "hello world!")


"hello world!"
"hello world!"

Stella [Top-Level]: (print *standard-tempo*)


60
60

Stella [Top-Level]: (print (+ 2 3 4))


9
9

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.

Stella [Top-Level]: (format t "I love Lisp!")


I love Lisp!
NIL

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

~& 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.

Example 7.2.1: The ~%FORMAT Directive


Stella [Top-Level]: (format t "~%this is the first line ~%and this is the second")

this is the first line

and this is the second

NIL

Example 7.2.2: The ~&FORMAT Directive


Stella [Top-Level]: (format t "~%this is the first line ~%~&and this is the second")
this is the first line
and this is the second
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.

Example 7.2.3: The ~A FORMAT Directive


Stella [Top-Level]: (format t "~%this is the first line ~&and this is the second ~&and the value of *standard-tempo*
~A" *standard-tempo*)
this is the first line
and this is the second
and the value of *standard-tempo* 60
NIL

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.

7.3 Using print and format in Common Music


Sometimes, it may be useful to monitor how Common Music assigns slots by printing values to
your computer monitor. An example of why you might want to do this is if you're just becoming
familiar with some aspect of Common Music. For example, let's say you'd like to learn more
about the Common Music function between. You look-up the documentation for between in the
Common Music Dictionary and find:

between lb ub &optional exclude state [Function]

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).

Example 7.3.1: print.lisp


(generator print midi-note ()
(setf note (item (notes c5 d5 e5 in palindrome) :kill T))
#|
:kill is a keyword argument to the item function.
A value of T means the item stream will terminate after it has completed its pattern.
A value of NIL means the item stream should not terminate.
|#
(setf duration (item (items .2 .3 .4)))
(setf rhythm (item (rhythms e e. q)))
(setf channel 0)
(setf amplitude (between .5 .9))
(print amplitude))

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.

Stella [Print]: mix


Mix objects: (<cr>=Print)

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.

Example 7.3.2: print-again.lisp


(algorithm print-again midi-note (length 6)
(setf note (item (notes c5 d5 e5 in palindrome)))
(setf duration (item (items .2 .3 .4)))
(setf rhythm (item (rhythms e e. q)))
(setf channel 0)
(setf amplitude (between .5 .9 amplitude))
(print amplitude))

Stella [Print-Again]: mix


Mix objects: (<cr>=Print-Again)
Start time offset:(<cr>=None)

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.

Example 7.3.3: format.lisp


(generator format midi-note (length 6)
(setf note (item (notes c5 d5 e5 in palindrome)))
(setf duration (item (items .2 .3 .4)))
(setf rhythm (item (rhythms e e. q tempo 120)))
(setf channel 0)
(setf amplitude (between .5 .9 amplitude))
(format t "~&note = ~a duration = ~a rhythm = ~a channel = ~a amplitude = ~5,f" note duration rhythm channel amplitude))

Stella [Format]: mix


Mix objects: (<cr>=Format)
Start time offset:(<cr>=None)

note = C5 duration = 0.2 rhythm = 0.25 channel = 0 amplitude = 0.607


note = D5 duration = 0.3 rhythm = 0.375 channel = 0 amplitude = 0.619
note = E5 duration = 0.4 rhythm = 0.5 channel = 0 amplitude = 0.777
note = E5 duration = 0.2 rhythm = 0.25 channel = 0 amplitude = 0.706
note = D5 duration = 0.3 rhythm = 0.375 channel = 0 amplitude = 0.781
note = C5 duration = 0.4 rhythm = 0.5 channel = 0 amplitude = 0.618
Another Common Music function we can explore using FORMAT is interp. The Common Music Dictionary
describes interp:

interp x env &key :scale :offset :return-type [Function]

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.

Example 7.3.4: format-again.lisp


(algorithm format-again midi-note (length 10 channel 0)
(setf note (interp (random 1.0) '(0 0 1 12) :offset 48 :return-type 'integer))
#|
interp selects a random number in the range 0 (inclusive) to 1 (exclusive). The random value is interpolated in the range using
the x,y pairs 0,0 to 1, 12. An offset of 48 is added to the interpolated value now in the range of 1 to 12. Interp returns an
integer.
|#
(setf rhythm (interp (random 1.0) '(0 0 1 2) :scale .75))
#|
interp selects a random value as it did for the note slot.
The random value is interpolated into the range 1 to 2.
The interpolated value is multiplied by .75.
|#
(setf duration rhythm)
(setf amplitude (between .5 .9))
(format t "~&note = ~a rhythm = ~5,F duration = ~5,F amplitude = ~5,F" note rhythm duration amplitude))

Stella [Format-Again]: mix


Mix objects: (<cr>=Format-Again)
Start time offset:(<cr>=None)

note = 57 rhythm = 1.053 duration = 1.053 amplitude = 0.589


note = 58 rhythm = 1.442 duration = 1.442 amplitude = 0.691
note = 49 rhythm = 1.208 duration = 1.208 amplitude =
0.552
note = 49 rhythm = 0.116 duration = 0.116 amplitude = 0.767
note = 56 rhythm = 1.442 duration = 1.442 amplitude = 0.884
note = 58 rhythm = 0.355 duration = 0.355 amplitude = 0.718
note = 49 rhythm = 0.804 duration = 0.804 amplitude =
0.661
note = 52 rhythm = 0.679 duration = 0.679 amplitude =
0.785
note = 57 rhythm = 0.381 duration = 0.381 amplitude = 0.582
note = 51 rhythm = 0.446 duration = 0.446 amplitude =
0.787

Your actual output may vary because of the RANDOM function.

7.4 Reading Data from the Computer Keyboard


The Common LISP function READ accepts input from the computer keyboard. Generally, READ is
used to assign a variable or slot. The READ template is:

(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.

Example 7.4.1: read.lisp


(generator read midi-note (length 5 channel 0 amplitude .5 duration .25 rhythm .5)
(format t "~&Enter a note number: ")
(setf note (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

Mix objects: (<cr>=Read)

Start time offset:(<cr>=None)

Enter a note number: 60


Enter a note number: 62
Enter a note number: 64
Enter a note number: 66
Enter a note number: 68

Stella [Read]: list


Read:
1. #<MIDI-NOTE | 60| 0.500| 0.250| 0.500| 0|>
2. #<MIDI-NOTE | 62| 0.500| 0.250| 0.500| 0|>
3. #<MIDI-NOTE | 64| 0.500| 0.250| 0.500| 0|>
4. #<MIDI-NOTE | 66| 0.500| 0.250| 0.500| 0|>
5. #<MIDI-NOTE | 68| 0.500| 0.250| 0.500| 0|>

Chapter 8: Variable Assignment and Scoping


This chapter introduces you to how to assign and reference variables in Common LISP and
Common Music. You will become familiar with several more Common LISP functions and the
concept of the scope of a variable.

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:

(SETF VARIABLE-1 VALUE-1 VARIABLE-2 VALUE-2VARIABLE-N VALUE-N)

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

Stella [Top-Level]: (defun double-the-tempo (tempo)


(* tempo 2))
DOUBLE-THE-TEMPO

Stella [Top-Level]: (double-the-tempo 80)


160

Stella [Top-Level]: (double-the-tempo *standard-tempo*)


120.0

Stella [Top-Level]: ,*standard-tempo*


60.0

Stella [Top-Level]: ,tempo


The symbol TEMPO has no value.

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.

When we call the function DOUBLE-THE-TEMPO with an input of *standard-tempo*, the


function returns the doubled value of the global variable *standard-tempo*. A subsequent query
of the variable *standard-tempo* indicates its value is unchanged. The global variable *standard-
tempo* is unchanged because it was not explicitly reassigned using SETF.

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

Stella [Top-Level]: ,*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

Stella [Top-Level]: ,*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.

Example 8.1.4: local-vs-global.lisp


(generator local-vs-global midi-note (length 10 channel 0 duration .5)
(setf note (item (notes c4 d e fs gs in heap)))
(setf amplitude (item (amplitudes ppp mp f in random)))
(setf rhythm (item (rhythms e q e.))))

#<GENERATOR: Local-Vs-Global>
Stella [Top-Level]: mix local-vs-global
Start time offset:(<cr>=None)

Stella [Top-Level]: list local-vs-global


Local-Vs-Global:
1. #<MIDI-NOTE | C4| 0.125| 0.500| 0.200| 0|>
2. #<MIDI-NOTE |FS4| 0.250| 0.500| 0.500| 0|>
3. #<MIDI-NOTE |GS4| 0.187| 0.500| 0.200| 0|>
4. #<MIDI-NOTE | D4| 0.125| 0.500| 0.200| 0|>
5. #<MIDI-NOTE | E4| 0.250| 0.500| 0.700| 0|>
6. #<MIDI-NOTE | C4| 0.187| 0.500| 0.200| 0|>
7. #<MIDI-NOTE | E4| 0.125| 0.500| 0.500| 0|>
8. #<MIDI-NOTE |GS4| 0.250| 0.500| 0.700| 0|>
9. #<MIDI-NOTE | D4| 0.187| 0.500| 0.700| 0|>
10. #<MIDI-NOTE |FS4| 0.125| 0.500| 0.200| 0|>

Stella [Top-Level]: ,note


The symbol NOTE has no value.

Notice in Example 8.1.4, the rhythm slot was calculated based on the current value of *standard-
tempo* which is 240 bpm.

8.2 LET and LET*


So far, we've created local variables in the argument list of a user-defined function. You may also
use the Common LISP function LET to create local variables.LET assigns local variables as
variable value pairs.
The template for LET is:
(LET ((VARIABLE-1 VALUE-1)
(VARIABLE-2 VALUE-2)
(VARIABLE-N VALUE-N))
(BODY-OF-LET))

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.

Example 8.2.1: average-of-three.lisp


(defun average-of-three (number1 number2 number3)
(let ((sum (+ number1 number2 number3)))
(list 'the 'average 'of number1 number2 number3 'is (/ sum 3.0))))

? (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.

Example 8.2.2: more-averaging.lisp

(defun more-averaging (number1 number2 number3


exclusive-upper-bound)
(let ((sum-of-numbers (+ number1 number2 number3))
(sum-of-random-numbers
(+ (random exclusive-upper-bound)
(random exclusive-upper-bound))))
(list 'average-of-numbers (/ sum-of-numbers 3.0) 'and 'average-of-random-numbers (/ sum-of-random-numbers 2.0))))

? (more-averaging 1 2.5 4 1.0)

(AVERAGE-OF-NUMBERS 2.5 AND AVERAGE-OF-RANDOM-NUMBERS


0.6019732842258612)

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 :

(LET* ((VARIABLE-1 VALUE-1)

(VARIABLE-2 VALUE-2)
(VARIABLE-N VALUE-N))

(BODY-OF-LET))

Example 8.2.3 implements a running average function using LET*.

Example 8.2.3: running-average.lisp


(defun running-average (number1 number2 number3)
(let* ((sum1 (+ number1 number2))
(average1 (/ sum1 2.0))
(average2 (/ (+ average1 number3) 2.0)))
(list 'running 'average 'is average2)))

? (running-average 1 2.5 4)

(RUNNING AVERAGE IS 2.875)

8.3 Common Music's Local Variables


Before beginning assignment of local variables in Common Music, let's first learn about some of
Common Music's local variables:count and time.

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.

Example 8.3.1: monitoring-count-and-time.lisp

(generator monitoring-count-and-time midi-note (start 0 end 3 length 5)


(setf note 60)
(setf rhythm .2)
(setf duration .1)
(setf amplitude .5)
(setf channel 0)
(format t "~%The current value of count is ~A and the current value of time is ~3,F" count time))

#<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.

Example 8.3.2: let-example.lisp

(generator let-example midi-note (length 5 channel 0)


(print count)
(setf note (item (notes c4 d e ef gs in heap)))
(let ((amp (/ (+ 1 count) 5.3)))
(setf amplitude amp))
(setf duration .5)
(setf rhythm .5))

#<GENERATOR: Let-Example>
Stella [Top-Level]: mix let-example
Start time offset:(<cr>=None)
0
1
2
3
4

Stella [Top-Level]: list let-example


Let-Example:
1. #<MIDI-NOTE | D4| 0.500| 0.500| 0.189| 0|>
2. #<MIDI-NOTE | E4| 0.500| 0.500| 0.377| 0|>
3. #<MIDI-NOTE | C4| 0.500| 0.500| 0.566| 0|>
4. #<MIDI-NOTE |GS4| 0.500| 0.500| 0.755| 0|>
5. #<MIDI-NOTE |DS4| 0.500| 0.500| 0.943| 0|>

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.

Example 8.3.3: let*-example.lisp

(generator let*-example midi-note (length 5 channel 0)


(format t "~&count=~A time=~3,F" count time)
(setf note (item (notes c4 d e ef gs in heap)))
(setf amplitude (item (items .1 .3 .46 .65 in random)))
(let* ((dur (/ (+ 1 count) (expt 2 count)))
(rhy dur))
; use the primitive FLOAT to convert from a ratio
; to floating point
(setf duration (float dur))

(setf rhythm (float rhy))))

#<GENERATOR: let*-Example>
Stella [Top-Level]: mix let*-example

Start time offset:(<cr>=None)

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

Stella [Top-Level]: list let*-example


Let*-Example:
1. #<MIDI-NOTE | C4| 1.000| 1.000| 0.300| 0|>
2. #<MIDI-NOTE |DS4| 1.000| 1.000| 0.300| 0|>
3. #<MIDI-NOTE |GS4| 0.750| 0.750| 0.300| 0|>
4. #<MIDI-NOTE | D4| 0.500| 0.500| 0.300| 0|>
5. #<MIDI-NOTE | E4| 0.312| 0.312| 0.460| 0|>

8.4 The Common Music vars Declaration


vars is a Common Music declaration that creates and assign variables that are local to a
particular container.vars takes the general form:

(vars (variable-1 value-1)


(variable-2 value-2)
(variable-n value-n))

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.4.1: vars.lisp

(generator vars midi-note (length 3 channel 0 rhythm .5 duration .2)


(vars (note-value 60)
(amplitude-value .75))
(setf note note-value)
(setf amplitude amplitude-value))

Stella [Vars]: mix


Mix objects: (<cr>=Vars)
Start time offset:(<cr>=None)

Stella [Vars]: list


Vars:
1. #<MIDI-NOTE | 60| 0.500| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 60| 0.500| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 60| 0.500| 0.200| 0.750| 0|>

Refer to Section 8.8 for further examples on the use of vars.

8.5 Assigning Local Variables Interactively using the


Computer Keyboard
In Section 7.4, we learned how Common LISP accepts data from the computer keyboard using
the function READ. We can use READ in conjunction with LET and LET * to interactively assign
local variables.
Example 8.5.1 is a Common LISP user-defined function, SIMPLE-ADD, that accepts two
numbers from the computer keyboard and assigns those numbers to local variables using the
Common LISP function LET.SIMPLE-ADD returns the sum of the two numbers.

Example 8.5.1: simple-add.lisp


(defun simple-add ()
(format t "~%Please enter a number ")
(let ((x (read)))
(format t "~%Please enter another number ")
(let ((y (read)))
(format t "~S plus ~S equals ~S" x y (+ x y)))))

Stella [Top-Level]: (simple-add)

PLEASE ENTER A NUMBER 6

PLEASE ENTER ANOTHER NUMBER 8


6 PLUS 8 EQUALS 14
NIL

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.

Example 8.5.2: interactive-assign.lisp

(generator interactive-assign midi-note (length 3 rhythm .5 duration .35 channel 0)


(format t "~%")
(format t "~&Please enter a note number: ")
(let ((the-note (read)))
(setf note the-note))
(format t "~&Please enter an amplitude: ")
(let ((the-amplitude (read)))
(setf amplitude the-amplitude)))

Stella [Interactive-Assign]: mix


Mix objects: (<cr>=Interactive-Assign)
Start time offset:(<cr>=None)

Please enter a note number: 60


Please enter an amplitude: .75

Please enter a note number: 48


Please enter an amplitude: .8

Please enter a note number: 36


Please enter an amplitude: .9

Stella [Interactive-Assign]: list


Interactive-Assign:
1. #<MIDI-NOTE | 60| 0.500| 0.350| 0.750| 0|>
2. #<MIDI-NOTE | 48| 0.500| 0.350| 0.800| 0|>
3. #<MIDI-NOTE | 36| 0.500| 0.350| 0.900| 0|>

8.6 Understanding Variable Scope in Common LISP


The scope of a variable is the region in which its value is known. We have seen variables that
have both local and global scope.

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 demonstrates the variable A is unassigned by generating an unbound variable


error.

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

? (let ((a 3))


(setf a (+ 1 a)))

? a
1

In Example 8.6.6, a global variable C is assigned a value of 0. We define a function INCREASE


that increases the value of C by a user-specified amount signified by the variable X.

Example 8.6.6

? (setf c 0)
0

? (defun increase (x)


(setf c (+ c x)))
;Compiler warnings :
; Undeclared free variable C (2 references), in INCREASE.
INCREASE

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

? (setf c (increase c 3))


6

? c
6

8.7 Understanding Variable Scope in Common Music


Now that we have a basic understanding of lexical context in Common LISP, let's see how these
concepts apply to Common Music.

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

Stella [Scope]: list


Scope:
1. #<MIDI-NOTE | 60| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 60| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 60| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 60| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 60| 0.350| 0.200| 0.750| 0|>

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.

In Example 8.7.4, we reassign the global variable X a value of 61.

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]: list


Scope:
1. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>

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>

Stella [Scope]: list


Scope:
1. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>

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]: list


Scope:
1. #<MIDI-NOTE | 62| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 62| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 62| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 62| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 62| 0.350| 0.200| 0.750| 0|>

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]: (defun add-one (z)


(setf z (+ 1 z)))
ADD-ONE

Stella [Scope]: (add-one 5)


6
Stella [Scope]: ,z
The symbol Z has no value.

In Example 8.7.9. we assign the global variable A a value of 1 using SETF.


Example 8.7.9

Stella [Scope]: (setf a 1)

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)))

Stella [Scope]: list


Scope:
1. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>

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]: list


Scope:
1. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
2. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
3. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
4. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>
5. #<MIDI-NOTE | 61| 0.350| 0.200| 0.750| 0|>

Stella [Scope]: ,h
The symbol H has no value.

8.8 Creating and Reading Item Streams


The Common Music function make-item-stream converts a list to an item stream. This function
is very useful because Common LISP has many primitives that operate on lists. We can use the
power of Common LISP to manipulate musical data represented as lists, convert the lists to item
streams, and output the item streams using Common Music.

The template for make-item-stream is:(make-item-stream type pattern items)

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.

The template for read-items is:(read-items stream)

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>

Stella [Top-Level]: (read-items my-item-stream)


(C4 D4 E4 FS4 R)

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.

Example 8.8.3: without-vars.lisp

(generator without-vars midi-note (length 6 channel 0)


(setf note (item (make-item-stream 'notes 'cycle '(c4 d4 e4 fs4 r))))
#|
The item stream is created each time the generator loops to create new events. Consequently, the only note that plays
is C4.
|#
(setf rhythm (item (rhythms e e. q q.)))
(setf amplitude (item (crescendo from pp to ff in 6)))
(setf duration rhythm)
(setf rhythm (+ .2 duration)))

Stella [Without-Vars]: mix


Mix objects: (<cr>=Without-Vars)
Start time offset:(<cr>=None)

Stella [Without-Vars]: list


Without-Vars:
1. #<MIDI-NOTE | C4| 0.700| 0.500| 0.300| 0|>
2. #<MIDI-NOTE | C4| 0.950| 0.750| 0.400| 0|>
3. #<MIDI-NOTE | C4| 1.200| 1.000| 0.500| 0|>
4. #<MIDI-NOTE | C4| 1.700| 1.500| 0.600| 0|>
5. #<MIDI-NOTE | C4| 0.700| 0.500| 0.700| 0|>
6. #<MIDI-NOTE | C4| 0.950| 0.750| 0.800| 0|>
In Example 8.8.4, we use the Common Music declaration vars to create an item stream and
assign it to the variable the-list. Notice that the output is the cyclic succession of notes C4, D4,
E4, FS4, a rest, and C4. Here we observe another important attribute of vars. Not only does vars
create variables that are local to a container, vars is evaluated only once- when the container is
scheduled to run. The cyclic note stream is assigned once when the generator is mix ed and the
item-stream-accessor item accesses each item in the item stream.

Example 8.8.4: with-vars.lisp


(generator with-vars midi-note (length 6 channel 0)
(vars
(the-list (make-item-stream 'notes 'cycle '(c4 d4 e4 fs4 r))))
(setf note (item the-list))
(setf rhythm (item (rhythms e e. q q.)))
(setf amplitude (item (crescendo from pp to ff in 6)))
(setf duration rhythm)
(setf rhythm (+ .2 duration)))

Stella [With-Vars]: mix


Mix objects: (<cr>=With-Vars)
Start time offset:(<cr>=None)

Stella [With-Vars]: list


With-Vars:
1. #<MIDI-NOTE | C4| 0.700| 0.500| 0.300| 0|>
2. #<MIDI-NOTE | D4| 0.950| 0.750| 0.400| 0|>
3. #<MIDI-NOTE | E4| 1.200| 1.000| 0.500| 0|>
4. #<MIDI-NOTE |FS4| 1.700| 1.500| 0.600| 0|>
5. #<REST 0.7#x4EDADD6>
6. #<MIDI-NOTE | C4| 0.950| 0.750| 0.800| 0|>

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.

The template for IF is:


(IF <TEST-CLAUSE> <T-CONSEQUENT> <NIL-CONSEQUENT>)

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:

? (setf *midi-note* 60)


60

? (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.

The template for a nested IF is:


(IF <TEST-CLAUSE> <T-CONSEQUENT>
(IF <TEST-CLAUSE> <T-CONSEQUENT> <NIL-CONSEQUENT>

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.

COND takes the general form:


(COND (<TEST-1> <CONSEQUENT-1>)
(<TEST-2> <CONSEQUENT-2>)
.
.
.
(<TEST-N> <CONSEQUENT-N>))

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

(defun test-range-with-cond (midi-note)


(cond ((< midi-note 0) 'too-low)
((> midi-note 127) 'too-high)
(T 'in-range)))

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.

The template for CASE is:


(CASE <KEY-FORM>
(<KEY-1> <CONSEQUENT-1A> <CONSEQUENT-1B> . . .<CONSEQUENT 1Z>)
(<KEY-2> <CONSEQUENT-2A> <CONSEQUENT-2B> . . .<CONSEQUENT 2Z>)
.
.
.
(<KEY-N> <CONSEQUENT-NA> <CONSEQUENT-NB> . . .<CONSEQUENT-NZ>))

In Example 9.3.1, we use CASE to write a function MIDI-RANGE-WITH-CASE that assigns a


value of 1 if a midi-note number is lower than the MIDI Specification (0-127), 2 if the midi-note
number is higher than the MIDI Specification, and 3 if the midi-note number is in range. We
return a quoted object indicating the status of the evaluation. Compare Example 9.3.1 with
Examples 9.1.2 and 9.2.1.

Example 9.3.1

(defun midi-range-with-case (midi-note)


(let ((result (if (< midi-note 0) 1
(if (> midi-note 127) 2 3))))
(case result

(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.

9.4 Common Music's Conditionals


Common Music has conditionals that monitor the status of an algorithmic composition. The
Common Music macros when-resting, unless-resting, when-chording, unless-chording, when-
ending, and unless-ending check the status of certain aspects of an algorithmic composition.
Recall the Common LISP macros WHEN and UNLESS from Chapter 6.WHEN evaluates its
consequent-clause if the condition evaluates to T.UNLESS evaluates its consequent-clause if the
condition evaluates to NIL. As you might guess, when-resting, when-chording, and when-
ending evaluate a consequent clause when Common Music is making a rest, generating a chord,
or is executing the last event of a container.unless-resting, unless-chording, and unless-ending
are the logical complements of these macros.

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.

Example 9.4.1: unless-resting.lisp

(generator unless-resting midi-note (length 6 channel 0)


(setf note (item (notes c4 d4 fs5 r r in random)))
(unless-resting
(setf amplitude (item (items .4 .5 .6))))
(setf rhythm amplitude)

(setf duration (+ rhythm .5)))

Stella [Unless-Resting]: list


Unless-Resting:
1. #<MIDI-NOTE | D4| 0.400| 0.900| 0.400| 0|>

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|>

6. #<MIDI-NOTE | C4| 0.400| 0.900| 0.400| 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

(generator unless-chording midi-note (length 6 channel 0)


(setf note (item (notes [c4 e g] [g4 b d5] r)))
(setf rhythm .65)
(setf duration .5)
(unless-chording
(setf amplitude (item (items .6 .7)))))

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.

Example 9.4.3: status.lisp

(algorithm status midi-note (length 6)


(setf note (item (notes [c4 d] e r [fs gs])))
(setf amplitude (item (items .1 .2 .3 .4)))
(setf rhythm .5)
(setf duration amplitude)

(format t "~&note = ~s time = ~s status = ~s" note time (status? :chording)))

Stella [Status]: mix


Mix objects: (<cr>=Status)
Start time offset:(<cr>=None)
note = C4 time = 0 status = NIL
note = D4 time = 0 status = T
note = E4 time = 0.5 status = NIL
note = R time = 1.0 status = NIL
note = FS4 time = 1.5 status = NIL
note = GS4 time = 1.5 status = T
note = C4 time = 2.0 status = NIL
note = D4 time = 2.0 status = T
note = E4 time = 2.5 status = NIL

9.5 Using Conditionals in Algorithmic Composition


Conditionals offer a powerful way to delineate form and sculpt musical events in the realization
of a compositional algorithm. In Example 9.5.1, we create a musical gesture of 99 note events
that change their pitch content during each third of the container. To achieve continuity, we
maintain the same intervallic distances between the pitches in each set. We use a pitch set that
follows the intervallic succession: M2 M2 m3 M2 M2. To achieve variety, we transpose the pitch
set up a M2 for the second third of the container and down a M2 for the last third of the
container.

Example 9.5.1: cond.lisp

(generator cond midi-note (channel 0 length 99)


(print count)
(cond ((< count 33) (setf note (item (notes c4 d4 g4 a4 in heap))))
((and (>= count 33) (<= count 65)) (setf note (item (notes d4 e4 g4 a4 in heap))))
(t (setf note (item (notes bf3 c4 ef4 f4 in heap)))))
(if (< count 49) (setf amplitude (item (items .1 .3 .5 in heap))) (setf amplitude (item (items .3 .5 .7))))
(setf rhythm (item (rhythms s s. e in heap tempo 160)))

(setf duration rhythm))

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

Example 9.5.2: case.lisp

(generator case midi-note (start 0 end 10 channel 0 duration .2 rhythm .2)


(case (round time)
(0 (setf note (item (notes c4 d e in heap)))
(setf amplitude (item (amplitudes pp p mp))))
(1 (setf note (item (notes c4 d e in heap)))
(setf amplitude (item (amplitudes pp p mp))))
(2 (setf note (item (notes cs4 ds f in heap)))
(setf amplitude (item (amplitudes p mp mf))))
(3 (setf note (item (notes d4 e fs in heap)))
(setf amplitude (item (amplitudes p mp mf))))
(4 (setf note (item (notes ds4 f g in heap)))
(setf amplitude (item (amplitudes p mp mf))))
(5 (setf note (item (notes e4 fs gs in heap)))
(setf amplitude (item (amplitudes mp mf f))))
(6 (setf note (item (notes f4 g a in heap)))
(setf amplitude (item (amplitudes mp mf f))))
(7 (setf note (item (notes fs4 gs as in heap)))
(setf amplitude (item (amplitudes mp mf f))))
(8 (setf note (item (notes g4 a b in heap)))
(setf amplitude (item (amplitudes mf f ff))))
(9 (setf note (item (notes gs4 as c5 in heap)))
(setf amplitude (item (amplitudes mf f ff))))
(t (setf note (item (notes a4 b cs5 in heap)))
(setf amplitude (item (amplitudes mf f ff))))))

[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.

9.6 Suggesting Listening


Matices Coincidentes (converging colors) composed by Pablo Furman was inspired by the use of
perspective in art and architectural design. Pablo Furman explores the convergence and
blending of tone colors using electronics. [Furman, 1998]

Chapter 10: Sets and Tables


A set is a collection of objects. In Common LISP, we can think of a set as a list. Each object in a
set is called an element or a member . In algorithmic composition, sets may be manipulated to
achieve a compositional result.

10.1 Introduction to Set Theory


The analysis of atonal music using set theory mathematics was codified by Allen Forte in his
landmark book, "The Structure of Atonal Music." [Forte, 1973] His theory of atonal music
develops a comprehensive framework for the organization of collection of pitches referred to as
pitch class sets . A pitch class is an integer in the range 0-11 that represents a symbolic note
n a m e . Pitch class assignment in twelve-tone equal temperament is C (or its enharmonic
equivalent) is 0, C-sharp (or its enharmonic equivalent) is 1, D-flat (or its enharmonic
equivalent) is 2. . . and so on. A pitch class set, or pc set, is a collection of integers representing
pitch classes.

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.

10.2 Set Operations


Common LISP provides a number of functions that perform operations on lists or sets. These
primitives are helpful in analyzing or composing music that is based on sets.APPEND, which was
first discussed in Chapter 3, is very helpful in manipulating sets.

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)

? (setf 5-2 '(0 1 2 3 5))


(0 1 2 3 5)

? (append 6-1 5-2)


(0 1 2 3 4 5 0 1 2 3 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

Example 10.2.2: append.lisp

(generator append midi-note (length 10 channel 0)


#|
We use vars to declare and assign variables.
The variable-value pairs in vars are evaluated once when the
container is scheduled to run.
|#
(vars (c-major '(c4 e4 g4))
(f-major '(f4 a4 c5))
(g-major '(g4 b4 d5))
(the-list (make-item-stream 'notes 'cycle
(append c-major f-major g-major '(c5)))))
#|
We use append to create a big list comprised of the symbolic
notes names assigned to the variables c-major, f-major and
g-major and the list '(c5). We use make-item-stream to create
an item stream of notes using the cycle pattern type
|#
(setf note (item the-list))
#|
We use the item-stream-accessor item to assign one element of
the item stream to the note slot.
|#
(setf rhythm (item (rhythms s s. s.. in heap)))
(setf duration (+ (* rhythm .5)))
(setf amplitude (item (crescendo from pp to ff in 10))))

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

Stella [Top-Level]: (read-items (retrograde (make-item-stream 'items 'cycle '(0 1 2 3 4 5))))


(5 4 3 2 1 0)

Example 10.2.5 uses REVERSE and retrograde in a Common Music generator.

Example 10.2.5: reverse.lisp


(generator reverse midi-note (length 9 channel 0)
(vars (c-major (reverse '(c4 e4 g4)))
;;; c-major is '(g4 e4 c4)

(f-major (reverse '(f4 a4 c5)))


;;; f-major is '(c5 a4 f4)

(g-major (reverse '(g4 b4 d5)))


;;; g-major is d5 b4 g4

(the-list (make-item-stream 'notes 'cycle


(append c-major f-major g-major))))
;;; the-list is g4 e4 c4 f4 a4 c5 d5 b4 g4
(format t "~% c-major ~a f-major ~a g-major ~a" c-major f-major g-major)
(setf note (item (retrograde the-list)))
#|
The retrograde of the list is g4 b4 d5 c5 a4 f4 c4 e4 g4
Why do we have to use the Common Music macro retrograde on
the the-list rather than reverse?
Because reverse does not expect an item stream as input.
|#
(setf rhythm (item (rhythms s s. s.. in heap)))
(setf duration (+ (* rhythm .5)))

(setf amplitude (item (crescendo from pp to ff in 9))))

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.

Example 10.2.7: nth.lisp


(generator nth midi-note (length 25 channel 0)
(let* ((index (random 6))
;;; return a random integer in the range 0-5

(6-Z36 '(0 1 2 3 4 7))


;;; 0 1 2 3 4 7 is set 6-Z36 according to
;;; Allen Forte's "Structure of Atonal Music"

(octave (nth (random 2) '(36 48))))


;;; use nth to return a random octave designation

(setf note (+ octave (nth index 6-Z36))))


;;; nth returns a random index into the set 6-Z36 and it is
;;; randomly transposed up either 3 or 4 octaves
(setf amplitude (item (amplitudes ppp pp p mp ff in heap)))
(setf rhythm .2)
(setf duration .3))

[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

? (member 3 '(0 1 2 3 5))


(3 5)

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

Example 10.2.9: member.lisp


(merge member ()
;;; the musical material is based on 4 sets:
;;; 6-Z3, 6-Z36, 5-Z38 and 7-Z38

(generator melody midi-note (length 101 channel 0)


(vars (6-Z36 '(0 1 2 3 4 7))
(6-Z3 '(0 1 2 3 5 6))
(5-Z38 '(0 1 2 5 8))
(7-Z38 '(0 1 2 4 5 7 8))
(notes1 (make-item-stream 'items 'heap
(member 2 6-Z36)))
(notes2 (make-item-stream 'items 'heap
(member 2 6-Z3)))
(notes3 (make-item-stream 'items 'heap
(member 2 5-Z38)))
(notes4 (make-item-stream 'items 'heap
(member 2 7-Z38))))
#|
Declare and assign the variables. Make an item stream from a subset of each set from pitch class 2: the largest pitch class that
all sets have in common.
|#
(if (< time 5) (setf note (+ 24 (item notes1)))
(if (and (>= time 5) (< time 10))
(setf note (+ 36 (item notes2)))
(if (and (>= time 10) (< time 15))
(setf note (+ 48 (item notes3)))
(setf note (+ 60 (item notes4))))))
#|
Use a nested if that monitors time to select which set is played.
|#
(setf amplitude (* (item (amplitudes pp p p mp ff fff in heap)) (/ count 101)))
;;; Scale the amplitudes over the length of the generator

(setf rhythm .2)


(setf duration rhythm))

(generator accompaniment midi-note (end 20 channel 0)


(if (< time 5) (setf note (item (items (chord 50 51 52 53 54 57))))
;;; Chord based on 6-Z36

(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 note (item (items


(chord 80 81 82 84 85 87 88)))))))
;;; Chord based on 7-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))

(generator final-chord midi-note (start 21 length 1)


(setf note (item (items (chord 12 25 38 51 64 77 90 103 116))))
#|
Final chord is based on the union of pitch classes from all four sets (0 1 2 3 4 5 6 7 8) or set 9-1.
Each pitch class is transposed up a successive octave (12 24 36 48 60 72 84 96 108)
|#
(setf amplitude .9)
(setf rhythm 1)
(setf duration 2)))

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.

In Example 10.2.10, we take the INTERSECTION of pc sets 6-Z3 and 6-Z36.

Example 10.2.10

? (intersection '(0 1 2 3 5 6) '(0 1 2 3 4 7))


(3 2 1 0)

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.12 sets.lisp


(generator sets midi-note (length 15 channel 0)
(vars (6-Z36 '(0 1 2 3 4 7))
(6-Z3 '(0 1 2 3 5 6))
(5-Z38 '(0 1 2 5 8))
(7-Z38 '(0 1 2 4 5 7 8))
(common-set1 (make-item-stream 'items 'heap
(intersection 6-Z36 6-Z3)))
(common-set2 (make-item-stream 'items 'heap
(intersection 5-Z38 7-Z38)))
(inclusive-set1 (make-item-stream 'items 'cycle
(union 6-Z36 6-Z3)))
(inclusive-set2 (make-item-stream 'items 'cycle
(union 5-Z38 7-Z38))))
(setf note (+ (item common-set1) 60))
(setf amplitude (* (item inclusive-set1) .1))
(if (= (item common-set2) 0) (setf rhythm (+ (* (item common-set2) .5) .01))
(setf rhythm (* (item common-set2) .5)))
(if (= (item inclusive-set2) 0) (setf duration (+ (* (item inclusive-set2) .5) .01))
(setf duration (* (item inclusive-set2) .5))))

The Common LISP primitive SET-DIFFERENCE performs set subtraction.SET-DIFFERENCE


takes two lists as input and subtracts all of the elements in the first list from those that are in
common with the second list.SET-DIFFERENCE returns a list.

Example 10.2.13
? (set-difference '(0 1 2 3 5 6) '(0 1 2 3 4 7))
(6 5)

? (set-difference '(0 1 2 3 4 7) '(0 1 2 3 5 6))


(7 4)

? (set-difference '(0 1 2 3) '(4 5 6 7))


(3 2 1 0)

? (set-difference '(0 1 2 3) '(0 1 2 3))


NIL

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

? (subsetp '(0 1 2 3 4 7) '(0 1 2 3 4 5 6 7 8))


T

? (subsetp '(0 1 2 3 4 7) '(0 1 2 5 8))


NIL

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.

Figure 10.3.1: A Simple Table

Pitch Class 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)))

We call the function to perform the look-up.

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.6: table.lisp


(generator table midi-note (length 15 channel 0)
(vars (table
'((0 C4)
(1 CS4)
(2 D4)
(3 DS4)
(4 E4)
(5 F4)
(6 FS4)
(7 G4)
(8 GS4 )
(9 A4)
(10 AS4)
(11 B4))))
(let* ((random-pitch-class (random 12))
(note-name (table-look-up random-pitch-class table)))
(setf note note-name))
(setf rhythm (item (rhythms s s. e e. in heap)))
(setf duration rhythm)
(setf amplitude (item (diminuendo from ff to pp in 15))))

Example 10.3.7 integrates many of the concepts we have learned in this chapter and applies
them to Common Music.

Example 10.3.7: table-with-sets.lisp


(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)))
#|
Generator table-with-sets creates a table of the first four pitch-class sets of cardinality 6. A random number selects one of four
keys in the range 1-4 that corresponds to sets 6-1, 6-2, 6-Z3 and 6-Z4. We use the function table-look-up to return the pitch
classes associated with the key. We assign a pitch-class to the note slot by randomly generating an index to the list which we
access using nth.
|#

(generator table-with-sets midi-note (length 60 channel 0)


(vars (table-cardinal6
'((1 (0 1 2 3 4 5) 6-1)
(2 (0 1 2 3 4 6) 6-2)
(3 (0 1 2 3 5 6) 6-Z3)
(4 (0 1 2 4 5 6) 6-Z4)))

(key1 (+ (random 4) 1))


(key2 (+ (random 4) 1))
(key3 (+ (random 4) 1))
(key4 (+ (random 4) 1))

(set1 (table-look-up key1 table-cardinal6))


(set2 (table-look-up key2 table-cardinal6))
(set3 (table-look-up key3 table-cardinal6))
(set4 (table-look-up key4 table-cardinal6)))
(let ((index (random 6)))
#|
generate a random number to access one pitch class in the set
|#
(cond ((< count 15) (setf note (+ (nth index set1) 40)))
((and (>= count 15) (< count 30))
(setf note (+ (nth index set2) 50)))
((and (>= count 30) (< count 45))
(setf note (+ (nth index set3) 60)))
(t (setf note (+ (nth index set4) 70)))))
(setf rhythm (item (items .2 .4 .5 in heap)))
(setf duration .1)
(setf amplitude (item (amplitudes pp mp mf ff in heap))))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/table-with-sets.mp3] table-with-sets.mp3

10.4 Suggested Listening


Late August by Paul Lansky is one in a series of his pieces that explores the conversations of
everyday life. In this composition, Paul Lansky recorded a conversation between two Chinese
students sometime in late August, 1989. Their processed speech, in conjunction with pitch
material motivated by set theory, became the foundation for this composition. [Lansky, 1990],
[Simoni, 1999]

Chapter 11: Arrays


11.1 Introduction to Arrays
An array is a data type that stores information in indexed locations. Arrays may store data in
multiple dimensions. Table 11.11.1 graphically represents a one-dimensional array named *MY-
MIDI-NOTES*.

Table 11.1.1:*MY-MIDI-NOTES*:

Index 0 Index 1 Index 2 Index 3

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.

Table 11.1.2 graphically represents a two-dimensional array named *MY-MIDI-NOTES-WITH-


RHYTHMS*:

Example 11.1.2:*MY-MIDI-NOTES-WITH-RHYTHMS*

Column 0 Column 1 Column 2 Column 3

Row 0 60 62 67 69

Row 1 's 'e 's 's.

Row 2 's. 's. 'e 'e.


The *MY-MIDI-NOTES-WITH-RHYTHMS* array has 4 columns identified as Column 0 through
Column 3. The array also has 3 rows identified as Row 0 through Row 2. Data in two-
dimensional arrays are indexed by row and column. For example, the value of the data at row 0,
column 2 is 67.

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

The symbols representing relative rhythms in the array *MY-MIDI-NOTES-WITH-RHYTHMS*


are quoted so that the symbols evaluate to themselves.

T h e MY-MIDI-NOTES-WITH-RHYTHMS array associates MIDI key numbers with particular


rhythms because of the organization of data in the array. There is an association between MIDI
note number 60 and the rhythm of a sixteenth (s) and dotted sixteenth (s.) note because the data
occupies Column 0.

11.2 Constructing Arrays


The Common LISP function MAKE-ARRAY is used to construct arrays.

The MAKE-ARRAY template for a one-dimensional array is:


(MAKE-ARRAY <SIZE> <:INITIAL-CONTENTS VALUE><:INITIAL-CONTENTS VALUE(S)>)

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)

In Example 11.2.4, we assign the global variable *MY-MIDI-NOTES* to be a one-dimensional


array with elements 60, 62, 67, and 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

? (make-array '(3 4) :initial-contents


'((60 62 67 69)
(s e s s.)
(s. s. e e.)))
#2a((60 62 67 69) (S E S S.) (S. S. E
E.))

The *MY-MIDI-NOTES-WITH-RHYTHMS* represented Table 11.1.2 array can be assigned using


SETF as shown in Example 11.2.9.

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))

11.3 Referencing Values in an Array


The Common LISP function AREF is used to reference the values stored in an array.
The template for AREF is:
(AREF <ARRAY-NAME> <ARRAY-LOCATION>)

Recall in Example 11.2.4 when we assigned the variable *MY-MIDI-NOTES* to a one-


dimensional array with elements 60, 62, 67, and 69. Example 11.3.1 demonstrates how we can
query the value of the second location of the array named *MY-MIDI-NOTES*.

Example 11.3.1
? (aref *my-midi-notes* 2)
67

Recall that in Example 11.2.8 we assigned the variable *MY-MIDI-NOTES-WITH-RHYTHMS* to


be a two dimensional array of midi-note numbers and symbolic rhythms.AREF may be used with
two-dimensional arrays. In Example 11.3.2, we query the value stored in the *MY-MIDI-NOTES-
WITH-RHYTHMS* array row 2, column 3.

Example 11.3.2
? (aref *my-midi-notes-with-rhythms* 2 3)
E.

11.4 Using Arrays in Common Music


Arrays are quite useful in algorithmic composition as they give a new way to organize and access
musical data. For example, let's create the MY-MIDI-NOTES-WITH-RHYTHMS array and
randomly access the data stored in the array using Common Music. For each randomly-selected
note, we randomly select a rhythm from the rhythms stored in the same column as the selected
note. For example, if MIDI note number 60 is selected, randomly select either a sixteenth or
dotted sixteenth note. Example 11.4.1 shows the Common Music implementation of this
algorithm.

Example 11.4.1: array.lisp


(generator array midi-note (length 10 channel 0 amplitude .5 duration .2)
(vars (my-midi-notes-with-rhythms (make-array '(3 4) :initial-contents
'((60 62 67 69)
(s e s s.)
(s. s. e e.)))))
(let ((row (+ 1 (random 2)))
(column (random 4)))
(setf note (aref my-midi-notes-with-rhythms 0 column))
(setf rhythm (rhythm (aref my-midi-notes-with-rhythms row column)))))

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.

Example 11.4.2: choose-larger.lisp

(defun choose-larger (array-size)


"Returns the larger of two randomly-generated numbers"
;;; Required for Example 11.4.3
(let ((x (random array-size))
(y (random array-size)))
(if (>= x y) x y)))

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.

Example 11.4.3: more-arrays.lisp

(generator more-arrays midi-note (start 0 end 10 duration .23 channel 0)


(vars (array-note
(make-array 4 :initial-contents
'(60 62 67 69)))
(array-rhythm
(make-array 3 :initial-contents
'(.06 .061 .062)))
(array-amplitude
(make-array 3 :initial-contents
'(0 .2 .3))))
(setf rhythm
(aref array-rhythm (choose-larger 3)))
(setf amplitude
(aref array-amplitude (choose-larger 3)))
(let ((octave (random 2)))
(setf note (+ (* 12 octave)
(aref array-note (choose-larger 4))))))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/more-arrays.mp3] more-arrays.mp3

11.5 Suggested Listening


Emma Speaks by Mary Simoni and Jason Marchant explores the application of Augmented
Transition Networks to the development of form in choreography and multimedia. A dance was
captured on video and edited such that the video post processing introduced another layer of
choreography. Music was composed for the edited video using Common Music (refer to example
on accompanying compact disk). [Simoni & Marchant, 1998]

Chapter 12: Applicative Programming


12.1 Introduction to Applicative Programming
Applicative programming is a technique that allows a function to be applied to each element of
a list. A simple example of applicative programming is to add 2 to every element of a list. For
example, the list (0 1 2 4) becomes (2 3 4 6). Applicative programming is a very simple yet
powerful way to transform lists.

12.2 Mapping a Function onto a List


The Common LISP primitive MAPCAR allows you to apply a function to each element of a list.
The applied function may be another Common LISP primitive or a user-defined function.
Essentially, MAPCAR allows you to pass a function to a function. In Common LISP, when you
pass a function to a function, the passed function must be proceeded by a #' (sharp-quote). The
template for MAPCAR is:

(MAPCAR #'FUNCTION '(LIST))

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)))

Using MAPCAR, we can determine if an entire list of MIDI notes is 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)

12.3 Lambda Expressions


Sometimes, you may have a procedure that you'd like to call on-the-fly. Such a procedure may be
one that is only used once so you don't want to bother giving it a name. Lambda expressions are
unnamed or anonymous functions.

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

Named Function (DEFUN ) Anonymous Function (LAMBDA )


(DEFUN FUNCTION-NAME(OPTIONAL-ARGUMENT-LIST) (LAMBDA(OPTIONAL-ARGUMENT-LIST)
FUNCTION DEFINITION)) FUNCTION DEFINITION))

Example 12.3.1 Example 12.3.1


(DEFUN MY-TRANSPOSE(NOTE INTERVAL) (LAMBDA(NOTE INTERVAL)
(+ NOTE INTERVAL)) (+ NOTE INTERVAL))

When a LAMBDA expression is encountered, the computer identifies it as an anonymous function


as seen in Example 12.3.2.

Example 12.3.2

? (lambda (note interval) (+ note interval))


#<Anonymous Function #x63D3C0E>

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.

Example 12.3.4: mapcar.lisp


(generator mapcar midi-note (length 30 channel 0)
(vars (6-Z36 (make-item-stream 'items 'cycle '(0 1 2 3 4 7)))
(5-1 (make-item-stream 'items 'cycle '(0 1 2 3 4)))
(5-4 (make-item-stream 'items 'cycle '(0 1 2 3 6)))
;;;
;;; Make item streams from sets 6-Z36, 5-1 and 5-4

(5-4-list '(0 1 2 3 6)))

(cond ((< count 10)


(setf note (invert (item 6-Z36) 30)))
((and (>= count 10) (< count 20))
(setf note (transpose (item 5-1) 64)))
(T (setf note (invert (item 5-4) 50))))
#|
The pitch material of the generator is divided into three sections. Section 1 uses set 6-Z36 that is inverted around MIDI
keynumber 30. Section 2 uses set 5-1 that is transposed up 64 half steps. Section 3 uses set 5-4 that is inverted around MIDI
keynumber 50. invert and transpose are Common Music functions.
|#
(let* ((index (mod count 5))
(amp-list (mapcar #'(lambda (N) (+ (* N .1) .1)) 5-4-list))
(amp (nth index amp-list)))
#|
The Common LISP primitive MOD accepts two inputs and returns the remainder after dividing the first argument by the second
argument.
|#
(setf amplitude amp))
#|
Create an index to read into the list. Because the list has five elements, we calculate the index as the current count mod which
cyclically returns integers 0, 1, 2, 3, 4.
We map a lambda expression onto the 5-4-list to scale each amplitude value by .1. We use nth with our index to return each
item in the list. The item returned from the list is assigned to the amplitude slot.
|#
(let ((rhy (float (/ count length))))
(if (< rhy .1)
(setf rhythm .1) (setf rhythm rhy)))
(setf duration .5))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/mapcar.mp3] mapcar.mp3

12.4 List Filtering


There are many Common LISP primitives that help filter lists by mapping a predicate onto each member of a list.
The template for using a list-filtering primitives is:
(PRIMITIVE #'PREDICATE LIST)

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))

The logical complement of FIND-IF is the Common LISP primitive REMOVE-IF.REMOVE-IF


returns every object that the predicate evaluates as NIL.

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

? (every #'(lambda (note) (and (>= note 0)


(<= note 127))) '(12 25 48 57))
T

12.5 Using Applicative Forms in Common Music

In Example 12.5.1 (applicative.lisp), we apply what we've learned about applicative


programming in Common LISP to Common Music. We use pc sets 4-4 (0 1 2 5), 4-5 (0 1 2 6),
and 4-6 (0 1 2 7) as a means of unifying the pitch content. These three sets are assigned using
vars. The three sets are combined to create a nested list. We create a list of all of the elements
found in 4-4, 4-5 and 4-6 using APPEND. An amplitude-set is assigned by applying FIND-IF to
the nested sets to search for set 4-4 using the user-defined predicate 4-4P. A rhythm set is
assigned by applying REMOVE-IF-NOT to the nested sets searching for a set that is not 4-5. We
have to take the FIRST of what is returned by REMOVE-IF-NOT because REMOVE-IF-NOT
returns a nested list. We assign a duration set from the appended sets. All sets are converted into
item streams. The note slot is conditionally assigned depending on how many events have been
generated. If the rhythm or duration slot generates a zero value, the rhythm and duration slots are
assigned a value of .3.

Example 12.5.1: applicative.lisp

(defun 4-4p (pitch-class)


"a predicate function that returns T is its input is set 4-4"
(not (set-difference pitch-class '(0 1 2 5))))
(defun 4-5p (pitch-class)
"a predicate function that returns T is its input is set 4-5"
(not (set-difference pitch-class '(0 1 2 6))))
#|
Generator applicative uses vars to assign 3 sets: 4-4, 4-5 and 4-6. These three sets are combined to create a nested list. We
then create a list of all of the elements found in 4-4, 4-5 and 4-6 using append. We assign an amplitude-set by applying find-if
to the nested sets. We assign a rhythm set by applying remove-if-not to the nested sets. We have to take the first of what is
returned by remove-if-not because a nested list is returned. We assign a duration set from the appended sets. These sets are
converted into item streams. The note slot is conditionally assigned dependent on how many events have been generated. If
the rhythm or duration slot is equal to zero, they are assigned a value of .3.
|#
(generator applicative midi-note (length 30)
(vars (4-4 '(0 1 2 5))
(4-5 '(0 1 2 6))
(4-6 '(0 1 2 7))
(nested-sets (list 4-4 4-5 4-6))
(appended-sets (append 4-4 4-5 4-6))
(amplitude-set (make-item-stream 'items 'cycle
(find-if #'4-4p nested-sets)))
(rhythm-set (make-item-stream 'items 'cycle
(first (remove-if-not #'4-5p nested-sets))))
(duration-set (make-item-stream 'items 'heap
appended-sets)))
(cond ((< count 10) (setf note
(transpose (nth (random 4) 4-4) 36)))
((< count 20) (setf note
(transpose (nth (mod count 4) 4-5) 47)))
(t (setf note (transpose (nth (random 4) 4-6) 58))))
(setf amplitude (+ .2 (* (item amplitude-set) .1)))
(if (zerop (item rhythm-set))
(setf rhythm .3)
(setf rhythm (* (item rhythm-set) .2)))
(if (zerop (item duration-set))
(setf duration .3)
(setf duration (* (item duration-set) .3))))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/applicative.mp3] applicative.mp3

Chapter 13: Recursion


13.1 Introduction to Recursion
Recursion is something that is defined in terms of itself. A recursive function is a function that
calls itself. A classic example of a recursive process is generating the Fibonacci series. In the
Fibonacci series, the first two terms are defined as 0 and 1. Thereafter, the next term is
calculated as the sum of the previous two terms. The mathematical relationship between terms
may be stated as:

next-term = (term - 1) + (term - 2)

For example, the six terms followed by the initial two terms of 0 and 1 are:

0 1 1 2 3 5 8 13 . . .

Another example of a recursive process is decrementing a given starting value by 1 until it is


equal to 0. We calculate the value of the next term as:

next-term = previous-term - 1

Given a starting value of 4, the resultant series is:


4 3 2 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.

13.2 Single-Test Tail Recursion


Single-test tail recursion is a method of recursion when a condition terminates processing and
the recursive function call occurs as the default case of a COND (e.g. the "tail end" of a COND ).
Example 13.2.1 shows the template for single-test tail recursion.

Example 13.2.1: Template for Single-Test Tail Recursion

(DEFUN FUNCTION-NAME (ARGUMENT)


(COND (END-TEST END-VALUE)
(T (FUNCTION-NAME (REDUCED ARGUMENT)))))

In addition to referencing a template when writing a recursive function, it is important to


understand how a recursive process works. Example 13.2.2 outlines some guidelines for writing
a recursive function.

Example 13.2.2: Guidelines for Writing a Recursive Function


1. What is the test that terminates the recursion? (e.g. end-test)
2. What do you want the function to return? (e.g. end-value)
3. How do you take one step? (e.g. reduced-argument)

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.

Example 13.2.4: recursive-dotimes.lisp


(defun recursive-dotimes (x)
(cond ((zerop x) 'done)
(t (print x) (recursive-dotimes (decf x)))))

Example 13.2.5: Output from Example 13.2.4


? (recursive-dotimes 4)
4
3
2
1
DONE

13.3 List-Consing Recursion


List-consing recursion is a recursive process that creates lists by consing a new element onto a
list. Example 13.3.1 shows a template for list-consing recursion.

Example 13.3.1: Template for List-Consing Recursion


(DEFUN FUNCTION-NAME (ARGUMENT)
(COND (END-TEST NIL)
(T (CONS NEW-ELEMENT(FUNCTION-NAME REDUCED-ARGUMENT)))))

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.

Example 13.3.2: recursive-make-chromatic-lick.lisp


(DEFUN RECURSIVE-MAKE-A-CHROMATIC-LICK(START NUMBER-OF-NOTES)
(COND ((ZEROP NUMBER-OF-NOTES) NIL)
(T (CONS START
(RECURSIVE-MAKE-A-CHROMATIC-LICK (+ START 1)(- NUMBER-OF-NOTES 1))))))

Example 13.3.3: Output from Example 13.3.2


? (recursive-make-a-chromatic-lick 60 6)
(60 61 62 63 64 65)

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.

Example 13.3.4: recursive-make-a-chromatic-lick.lisp


(defun recursive-make-a-chromatic-lick (start number-of-notes)
(cond ((zerop number-of-notes) nil)
(t (format t "~&start = ~A number-of-notes = ~A" start number-of-notes) (cons start
(recursive-make-a-chromatic-lick (incf start)
(decf number-of-notes))))))

Example 13.3.5: Output from Example 13.3.4


? (recursive-make-a-chromatic-lick 60 6)
start = 60 number-of-notes = 6
start = 61 number-of-notes = 5
start = 62 number-of-notes = 4
start = 63 number-of-notes = 3
start = 64 number-of-notes = 2
start = 65 number-of-notes = 1
(60 61 62 63 64 65)

13.4 Conditional Augmenting Tail Recursion


Conditional augmenting tail recursion is a function that conditionally increments a value or
conses an element to a list. Example 13.4.1 shows the template for conditional augmenting tail
recursion.
Example 13.4.1: Template for Conditional Augmenting Tail Recursion

(DEFUN FUNCTION-NAME (ARGUMENT)


(COND (END-TEST END-VALUE)
(AUGMENT-CONDITION (AUGMENT-FUNCTION AUGMENT-VALUE (FUNCTION-NAME REDUCED-
ARGUMENT)))
(T (FUNCTION-NAME REDUCED-ARGUMENT))))

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.

Example 13.4.2: recursive-count-outlyers.lisp

(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 (recursive-count-outlyers (rest the-list) result))))

Example 13.4.3: Output from Example 13.4.2


? (recursive-count-outlyers
'(87 67 129 776 43) 0)
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))))

Example 13.4.5: Output from Example 13.4.4


? (recursive-count-outlyers
'(87 67 129 776 43) 0)
result = 0 the-list = (87 67 129 776 43)
result = 0 the-list = (67 129 776 43)
result = 1 the-list = (129 776 43)
result = 2 the-list = (776 43)
result = 2 the-list = (43)
2

13.5 Double Test Tail Recursion


Double test tail recursion is a function that terminates based on one of two conditions. Example
13.5.1 shows the template for double test tail recursion.

Example 13.5.1: Template for Double Test Tail Recursion

(DEFUN FUNCTION-NAME (ARGUMENT)


(COND (END-TEST1 END-VALUE1)
(END-TEST2 END-VALUE2)
(T (FUNCTION-NAME REDUCED-ARGUMENT))))

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.

Example 13.5.2: recursive-find-first-outly.lisp

(defun recursive-find-first-outlyer (midi-note-list)


(cond ((null midi-note-list) nil)
((or (< (first midi-note-list) 0)
(> (first midi-note-list) 127))
(first midi-note-list))
(t (recursive-find-first-outlyer
(rest midi-note-list)))))

Example 13.5.3: Output from Example 13.5.2


? (recursive-find-first-outlyer '(87 67 129 776 43))
129

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.

Example 13.5.5: recursive-find-first-outlyer.lisp


(defun recursive-find-first-outlyer (midi-note-list)
(cond ((null midi-note-list) nil)
((or (< (first midi-note-list) 0)
(> (first midi-note-list) 127))
(first midi-note-list))
(t (format t "~&midi-note-list = ~a"
midi-note-list) (recursive-find-first-outlyer
(rest midi-note-list)))))

Example 13.5.6: Output from Example 13.5.5


? (recursive-find-first-outlyer '(87 67 129 776 43))
midi-note-list = (87 67 129 776 43)
midi-note-list = (67 129 776 43)
129

13.6 Multiple Recursion


A function is categorized as multiple recursion if it makes more than one recursive call at each
pass of the function. Generating the Fibonacci series requires multiple recursion because the
next term is derived by the sum of the previous two terms (n - 2) + (n - 1). Example 13.6.1 shows
the template for multiple recursion.

Example 13.6.1: Template for Multiple Recursion

(DEFUN FUNCTION-NAME (ARGUMENT)


(COND (END-TEST1 END-VALUE1)
(END-TEST2 END-VALUE2)
(T (COMBINER(FUNCTION-NAME FIRST-REDUCED-ARGUMENT)
(FUNCTION-NAMESECOND-REDUCED-ARGUMENT)))))

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.

Example 13.6.2: fibonacci.lisp


(defun fibonacci (x)
(format t "~&x = ~s" x)
(cond ((zerop x) 0)
((= x 1) 1)
(t (+ (fibonacci (- x 1)) (fibonacci (- x 2))))))

Example 13.6.3: Output from Example 13.6.2


? (fibonacci 5)
x = 5
x = 4
x = 3
x = 2
x = 1
x = 0
x = 1
x = 2
x = 1
x = 0
x = 3
x = 2
x = 1
x = 0
x = 1
5

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]

Figure 13.6.1: Graphic representation of (FIBONACCI 5)

13.7 The LISP Debugger


Up to this point, we have tried to stay out of the debugger. The debugger can be a helpful place to learn how
Common LISP handles things. To invoke the debugger, use the Common LISP BREAK function. The template for
the BREAK function is:
(BREAK "OPTIONAL STRING")

Example 13.7.2 can be modified to use BREAK as seen in Example 13.7.2.

Example 13.7.2

(defun fibonacci (x)


(cond ((zerop x) 0)
((= x 1) 1)
(t (break "right before a recursive call")
(+ (fibonacci (- x 1))
(fibonacci (- x 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.

13.8 Using Recursive Forms in Common Music


Example 13.8.1 integrates many of the recursive techniques described in this chapter in
Common Music. The function RECURSIVE-MAKE-A-CHROMATIC-LICK uses list-consing
recursion to generate a specified number of chromatic notes. The function RANGE-LIMITED-
RANDOM-NUMBERS uses tail recursion to generate a specified number of random numbers is a
user-specified range. The function COUNT-IN-RANGE uses conditional augmenting tail
recursion to count the number of notes that fall within a user-specified range (lower and upper
bound inclusive).

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.

Example 13.8.1: recursion-etude.lisp

#|
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))

13.9 Suggested Listening


On Growth and Form by Bruno Degazio for mixed ensemble and electronic sounds explores the
recursive structure of fractals. As the composer states, "Drawing on the chaotic energy of fractal
processes, On Growth and Form is a sort of ritual dance of life, the by-product of a recursive
explosion of musical events." [Degazio, 1992]

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]

Chapter 14: Iteration


14.1 Introduction to Iteration
Iteration is a repeating process. In Common LISP, iteration is accomplished through loops.
Loops repeat a process over and over again. A loop should terminate after a specified number of
repetitions or when a condition is met. Common LISP has a multitude of iterative forms.
Because there are so many, this chapter will use many of the same examples used in Chapter 13
so the reader can easily compare recursive and iterative forms.

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.

The template for DOTIMES is:


(DOTIMES (<LOOP-CONTROL-VARIABLE> <UPPER-BOUND> <OPTIONAL-RESULT>) <BODY>)

In Example 14.2.1, we use DOTIMES to increment the loop-control-variable COUNTER. As we


enter the loop, the variable COUNTER starts counting at 0. We enter the body of the loop and
print the value of COUNTER. Because the value of COUNTER is less than the upper-bound,
COUNTER increments by 1 and the body of the loop is executed again. The iterative process stops
when COUNTER equals the upper-bound.

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

? (dotimes (counter 4 'done)


(print counter))
0
1
2
3
DONE

In Example 14.2.3, we define a function MAKE-A-CHROMATIC-LICK. The purpose of the


function is to create a list that generates a specified number of half steps from a starting
keynumber. We begin by entering a LET that initializes the variable THE-LIST to NIL. The
notes we generate will be collected into THE-LIST. The body of the LET consists of a DOTIMES.
The loop-control variable is INDEX, the upper-bound is the NUMBER-OF-NOTES we need to
create, and the result is THE-LIST of notes. In the body of the DOTIMES, we CONS the sum of
STARTING-NOTE and the INDEX onto the list. We re-assign the variable THE-LIST using SETF.
The list consing process continues until the variable INDEX is equal to the NUMBER-OF-NOTES.
The consed list is returned. To help understand the iterative process, a FORMAT statement has
been inserted into the body of the dotimes.

Example 14.2.3: make-a-chromatic-lick.lisp

(defun make-a-chromatic-lick (starting-note number-of-notes)


(let ((the-list nil))
(dotimes (index number-of-notes the-list)
(setf the-list (cons (+ index starting-note) the-list))
(format t "~%index = ~a the-list = ~a" index the-list))))

When we call the function, we get the following results:

? (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)
(65 64 63 62 61 60)

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

? (defun make-a-chromatic-lick (starting-note number-of-notes)


(let ((the-list nil))
(dotimes (index number-of-notes (reverse the-list))
(setf the-list (cons (+ index starting-note)
the-list))
(format t "~%index = ~a the-list = ~a" index
the-list))))

We call the function:

? (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.

The template for DOLIST is:


(DOLIST (<LOOP-CONTROL-VARIABLE> <LIST> <OPTIONAL-RESULT>) <BODY>)

Example 14.3.1 shows a simple application of DOLIST.

Example 14.3.1:

? (dolist (counter '(60 61 62 63) 'done)

(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.

Example 14.3.2: count-outlyers.lisp

(defun count-outlyers (midi-note-list)


(let ((result 0)
(lower-bound 0)
(upper-bound 127))
(dolist (element midi-note-list result)
(when (or (< element lower-bound)
(> element upper-bound))
(incf result))
(format t "~%element = ~a result = ~a" element result))))
When we call the function, we get the following results:

? (count-outlyers '(189 -5 129 78 64))


element = 189 result = 1
element = -5 result = 2
element = 129 result = 3
element = 78 result = 3
element = 64 result = 3
3

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.

Example 14.4.3: return-first-outlyer.lisp

(defun return-first-outlyer (midi-note-list)


(dolist (element midi-note-list 'none-found)
(if (or (< element 0) (> element 127)) (return element))))

We call the function:


? (return-first-outlyer '(0 98 -5 129))
-5

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)

(VARIABLE-2 INIT-VALUE2 OPTIONAL-UPDATE-VALUE2)


(VARIABLE-N INIT-VALUEN OPTIONAL-UPDATE-VALUEN))
(LOOP-TERMINATION-TEST CONSEQUENT-1 CONSEQUENT-2 . . . CONSEQUENT-N)
BODY)

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

? (do ((counter 0 (incf counter)))


((= counter 4) 'done)
(print counter))

0
1
2
3
DONE

We enter the DO. We initialize the variable COUNTER to 0. The loop-termination-test (=


COUNTER 4) is evaluated. If the evaluation is false, then DO executes its body and prints the
value of counter. On the next iteration, DO updates the variable COUNTER by incrementing it by
one. The loop-termination-test is evaluated and because it evaluates to false, the body is
executed again. This process repeats until the value of COUNTER is equal to 4. The consequent
clause on loop termination evaluates the quoted symbol 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

? (do ((the-list '(60 61 62 63))


(index 0 (incf index)))
((= index (length the-list)) 'done)
(print (nth index the-list)))

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.3: count-outlyers-do.lisp

(defun count-outlyers-do (midi-note-list)


(do ((element 0 (incf element))
(result 0))
((= element (length midi-note-list)) result)
(when (or (< (nth element midi-note-list) 0)
(> (nth element midi-note-list) 127))
(incf result))
(format t "~%element = ~a result = ~a" element result)))

We call the function:

? (count-outlyers '(189 -5 129 78 64))


element = 189 result = 1
element = -5 result = 2
element = 129 result = 3
element = 78 result = 3
element = 64 result = 3
3

In Example 14.5.5, we modify the function in Example 14.2.3 MAKE-A-CHROMATIC-LICK, to


use DO.

Example 14.5.5

? (DEFUN MAKE-A-CHROMATIC-LICK-DO (STARTING-NOTE NUMBER-OF-NOTES)


(DO ((INDEX 0 (INCF INDEX))
(THE-LIST NIL (CONS (+ (- INDEX 1) STARTING-NOTE) THE-LIST)))
((= INDEX NUMBER-OF-NOTES) THE-LIST)))

We call the function MAKE-A-CHROMATIC-LICK-DO with inputs of 60 and 6. The function


returns a list of half steps starting on 60 and going for 6 in descending order.

? (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.

Example 14.6.1: return-first-outlyer-do*.lisp

(defun return-first-outlyer-do* (midi-note-list)


(do* ((index 0 (incf index))
(element (nth index midi-note-list) (nth index
midi-note-list)))
((= index (length midi-note-list)) 'none-found)
(if (or (< element 0) (> element 127))
(return element))))

We call the function:

? (return-first-outlyer-do* '(0 98 -5 129))


-5

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.

14.7 LOOP with WHEN and UNLESS


In contrast to DO and DO*, Common LISP supports a very simple iterative form called LOOP. The template for
LOOP is:
(LOOP <BODY>)

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

(defun make-a-chromatic-lick-loop (starting-note


number-of-notes)
(let ((the-list nil)
(index 0))
(loop (when (= index number-of-notes) (return the-list))
(setf the-list (cons (+ index starting-note)
the-list))

(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.

We call the function:

? (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.

Example 14.7.2: count-outlyers-loop.lisp

(defun count-outlyers-loop (midi-note-list)


(let ((element 0)
(result 0))
(loop (unless (< element (length midi-note-list))
(return result))
(if (or (< (nth element midi-note-list) 0)
(> (nth element midi-note-list) 127))
(incf result))

(incf element))))

We call the function:

? (count-outlyers-loop '(189 -5 129 78 64))


3

14.8 Reading and Writing Records using Iteration


In Chapter 7, we learned how to input data into a program from the computer keyboard using
the Common LISP primitive READ. Common LISP provides the macro WITH-OPEN-FILE that
allows you to read data from a file. The template for WITH-OPEN-FILE is:

(WITH-OPEN-FILE (VARIABLE "PATH-TO-DATA-FILE"))

VARIABLE is a variable that is associated with the stream of data that is read from the file.

The exact specification of the PATH-TO-DATA-FILE is dependent on your operating system.

The following code assumes you are using the Macintosh Operating System (MacOS) and that
the file midi.dat resides on the desktop.

(WITH-OPEN-FILE (MY-DATA "MACINTOSH HD:DESKTOP FOLDER:MIDI.DAT"))

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.

Example 14.8.1: get-midi-data.lisp

(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.

In Example 14.8.2, we call the function GET-MIDI-DATA.

Example 14.8.2

? (get-midi-data)

The midi note is 60 the amplitude is 98 and the channel is 0


NIL

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.

Example 14.8.4: read-multiple-records.lisp

(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))))

In Example 14.8.5, we call the function READ-MULTIPLE-RECORDS.

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.

Example 14.8.6: write-multiple-records.lisp

(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)))))

In Example 14.8.7, we call the function WRITE-MULTIPLE-RECORDS.

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:

midi-note = 60 velocity = 98 channel 0


midi-note = 34 velocity = 87 channel 1
midi-note = 98 velocity = 78 channel 2

14.9 Using Iterative Forms in Common Music


We have used generators, algorithms, and merges in most of the Common Music examples thus
far. Merges were used to schedule generators. We found that generators and algorithms have an
implicit iterative nature as they step through item streams in the assignment of slots.

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.

Example 14.9.1: chromatic-lick-stella.lisp


(thread chromatic-lick-stella ()
(do ((my-note 60 (incf my-note))
(my-rhy (+ (random 2.0) 1) (+ (random 2.0) 1)))
((> my-note 65))
(object midi-note
note my-note
rhythm my-rhy
duration .1
amplitude .75
channel 0)))

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.

Example 14.9.2: c-chord.lisp

(thread C-chord (start 0)


(dolist (chord-member '(60 64 67))
(object midi-note
note chord-member
rhythm 0
duration .1
amplitude .75
channel 0)))

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.

Example 14.9.3: simple-example.lisp


(merge simple-example ()
#|
in-tempo is a Common Music function that resets the value of *standard-tempo*
|#
(in-tempo 80)
#|
Create a thread for the melody. We use an index to step through a list of notes and a list of rhythms.
We isolate each element of the note list and rhythm list using nth. Because the rhythms are relative durations,
they must be converted to absolute duration before being assigned to the rhythm or duration slot. The
conversion from relative duraiton to absolute duration is accomplished using the Common Music function rhythm.
A midi-note is created and the slots are assigned based on the initialization and update values of the do loop.
Iterative processing terminates when the index is equal to the length of the note-list less one.
|#
(thread melody ()
(do* ((index 0 (incf index))
(note-list '(e4 c4 fs4 f4 e4 gs4 cs5 c5))
(rhy-list '(s e s s 64 64 64 h))
(a-note (nth index note-list) (nth index note-list))
(a-rhy (rhythm (nth index rhy-list)) (rhythm (nth index rhy-list))))
((= index (- (length note-list) 1)) 'finished)
(object midi-note
note a-note
rhythm a-rhy
duration a-rhy
amplitude .65
channel 0)))
#|
A thread is created to harmonize the previous thread. The chord starts at 1 second.
A dolist steps through the chord members assigning each one to the note slot. The rhythm
slot has a value of 0 indicating the notes will be played simultaneously.
|#
(thread harmony (start 1)
(dolist (chord-member '(c2 fs3 cs5))
(object midi-note
note chord-member
rhythm 0
duration 1
amplitude .68
channel 0))))

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/simple-example.mp3] simple-
example.mp3

Chapter 15: Algorithmic Composition Using


Probabilistic Methods
15.1 Introduction to Probability
Chapter 4 introduced the random item stream pattern type. Using the random item stream
pattern type, we were able to control the probability that a certain event would be selected.

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%).

15.2 Random Item Stream


The random item stream pattern type uses the optional keyword :weight to alter the probability
of an event being selected in relation to the other events in the item stream.

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 Probability Table

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

(setf rhythm (item (rhythms q s e in random)))

Table 15.2.2 shows that the selection of rhythms based on equal probability.

Table 15.2.2

Rhythm Probability Table

Rhythm Q S E

Weight .333 .333 .333

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.

Example 15.2.4: random.lisp

(generator random midi-note (length 12 channel 0)


(setf note (item (notes (c4 :weight .5) d4 e4 in random)))
(setf rhythm (item (rhythms q s e in random)))
(setf amplitude (item (items (.1 :max 1) (.2 :max 1)
(.3 :min 2) (.4 :min 2) in random)))
(setf duration rhythm))

Stella [Random]: list


Random:
1. #<MIDI-NOTE | C4| 0.500| 0.500| 0.200| 0|>
2. #<MIDI-NOTE | D4| 0.500| 0.500| 0.100| 0|>
3. #<MIDI-NOTE | E4| 0.500| 0.500| 0.300| 0|>
4. #<MIDI-NOTE | D4| 1.000| 1.000| 0.300| 0|>
5. #<MIDI-NOTE | E4| 0.250| 0.250| 0.200| 0|>
6. #<MIDI-NOTE | D4| 0.250| 0.250| 0.100| 0|>
7. #<MIDI-NOTE | D4| 0.500| 0.500| 0.200| 0|>
8. #<MIDI-NOTE | E4| 0.500| 0.500| 0.300| 0|>
9. #<MIDI-NOTE | C4| 0.250| 0.250| 0.300| 0|>
10. #<MIDI-NOTE | D4| 0.250| 0.250| 0.200| 0|>
11. #<MIDI-NOTE | C4| 0.250| 0.250| 0.400| 0|>
12. #<MIDI-NOTE | E4| 1.000| 1.000| 0.400| 0|>

15.3 Graph Item Stream


The graph item stream pattern type creates an item stream of user-specified rules for traversing
a series of nodes called a graph.

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,

(e4 to (items c4 ds4 in cycle))

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.

The graph in Figure 15.3.1 may be represented in Common Music as

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.

Example 15.3.3: graph.lisp


(generator graph midi-note (channel 0 length 20)
(setf note (item (notes (c4 to (items e4 ds4 in random))
(e4 to (items c4 ds4 in cycle))
(ds4 to (items c4 e4 in palindrome))
in graph)))
(setf rhythm (item (items .2 .4 .6)))
(setf amplitude (item (items .2 .4 .6 .8)))
(setf duration rhythm))

15.4 Markov Item Stream


A markov process is 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 .

Table 15.4.1: A first-order transition table

C4 D4 E4

C4 .10 .75 .15

D4 .25 .10 .65

E4 .50 .30 .20

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.

Table 15.4.2: A first-order transition table that loops

C4 D4 E4

C4 ——— .5 .5

D4 ——— ——— 1.0

E4 ——— 1.0 ———

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.

Table 15.4.3: A first-order transition table that dead-ends

C4 D4 E4 F4

C4 ——— .5 .25 .25

D4 ——— ——— 1.0 ———

E4 ——— 1.0 ——— ———

The Common Music macro markov may be used to generate an item stream that constructs a
markov chain. The markov macro template is:

(markov (past-event -> (outcome-1 weight-1) (outcome-2 weight-2) . . . (outcome-n weight-


n)))

Example 15.4.1 uses markov to assign the note slot using the first-order transition table
described in Table 15.4.1.

Example 15.4.1: markov1.lisp

(generator markov1 midi-note (channel 0 amplitude .7 rhythm .5 duration .5 length 12)


(setf note (item
(markov
(C4 -> (C4 0.1) (D4 0.74) (E4 0.15))
(D4 -> (C4 0.25) (D4 0.1) (E4 0.65))
(E4 -> (C4 0.5) (D4 0.3) (E4 0.2))))))

When markov1.lisp is evaluated, the following note series is generated:

Figure 15.4.1: Output from markov1.lisp

[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.

Figure 15.4.2: Aunt Rhody


[https://quod.lib.umich.edu/s/spobooks/images/bbv9810.0001.001-00000032.jpg]

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.

(F5 -> (E5 1.0))

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))

Evaluating the code in 15.4.2 returns the following:

Example 15.4.3 Output from Example 15.4.2

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))

Evaluating the code in 15.4.3 returns the following:

Example 15.4.4 Output from Example 15.4.3

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 ——-

(MARKOV (E E -> (Q 0.5) (E 0.3333333333333333) (H


0.16666666666666666))
(E Q -> (E 1.0))
(Q E -> (Q 1.0))
(Q Q -> (Q 0.4) (E 0.6))
(Q H -> (E 1.0))
(H E -> (Q 1.0)))

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.

Example 15.4.5 markov2.lisp


(generator markov2 midi-note (start 0 channel 0 amplitude .7 length 22)
(setf note (item (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)))))
(setf rhythm (rhythm (item (MARKOV (E E -> (Q 0.5)
(E 0.3333333333333333) (H 0.16666666666666666))
(E Q -> (E 1.0))
(Q E -> (Q 1.0))
(Q Q -> (Q 0.4) (E 0.6))
(Q H -> (E 1.0))
(H E -> (Q 1.0))))))
(setf duration rhythm))

Figure 15.4.3: Output from Example 15.4.5

[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]: list


Top-Level:
1. #<THREAD: From-Test>

Stella [Top-Level]: go 1

Focus: From-Test
Type: Thread
Status: Normal
Objects: 22
Start: unset

Stella [From-Test]: list


From-Test:
1. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
2. #<MIDI-NOTE | D5| 1.000| 1.000| 0.693| 0|>
3. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
4. #<MIDI-NOTE | C5| 1.000| 1.000| 0.693| 0|>
5. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
6. #<MIDI-NOTE | E5| 1.000| 1.000| 0.693| 0|>
7. #<MIDI-NOTE | E5| 0.500| 0.500| 0.693| 0|>
8. #<MIDI-NOTE | D5| 1.000| 1.000| 0.693| 0|>
9. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
10. #<MIDI-NOTE | D5| 1.000| 1.000| 0.693| 0|>
11. #<MIDI-NOTE | C5| 0.500| 0.500| 0.693| 0|>
12. #<MIDI-NOTE | C5| 1.000| 1.000| 0.693| 0|>
13. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
14. #<MIDI-NOTE | E5| 1.000| 1.000| 0.693| 0|>
15. #<MIDI-NOTE | E5| 0.500| 0.500| 0.693| 0|>
16. #<MIDI-NOTE | D5| 1.000| 1.000| 0.693| 0|>
17. #<MIDI-NOTE | E5| 0.500| 0.500| 0.693| 0|>
18. #<MIDI-NOTE | D5| 1.000| 1.000| 0.693| 0|>
19. #<MIDI-NOTE | D5| 0.500| 0.500| 0.693| 0|>
20. #<MIDI-NOTE | E5| 1.000| 1.000| 0.693| 0|>
21. #<MIDI-NOTE | E5| 0.500| 0.500| 0.693| 0|>
22. #<MIDI-NOTE | E5| 1.000| 1.000| 0.693| 0|>

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

(MARKOV (D5 D5 -> (D5 0.4) (C5 0.4) (E5 0.2))


(D5 C5 -> (E5 1.0))
(D5 E5 -> (D5 0.75) (E5 0.25))
(C5 D5 -> (D5 0.5) (C5 0.5))
(C5 C5 -> (D5 1.0))
(E5 D5 -> (E5 0.75) (D5 0.25))
(E5 E5 -> (D5 0.75) (E5 0.25)))

15.5 1/f 2 Noise or Brownian Motion


Brownian motion is the observed movement of small particles randomly bombarded by the
molecules of the surrounding medium. The phenomenon was first observed by the biologist
Robert Brown and was eventually explained by Albert Einstein. Brownian motion is also referred
to as 1/f 2 noise.

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.

Example 15.5.1: brownian-motion.lisp

(defun brownian-motion (start number-of-notes)


(do* ((counter 0 (incf counter))
(the-array (make-array 7 :initial-contents
'(-3 -2 -1 0 1 2 3)))
(note start (+ note (aref the-array (random 7))))
(the-list (cons start ()) (cons note the-list)))

((= counter (- number-of-notes 1)) (reverse 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

Example 15.5.2: brownian-music.lisp

(generator brownian-music midi-note (start 0 length 10


rhythm .4 duration .5 amplitude .7 channel 0)
(vars (brown-item-stream (make-item-stream 'items 'cycle (brownian-motion 60 6))))
(setf note (item brown-item-stream)))

The output of the generator is as follows:

Stella [Brownian-Music]: list


Brownian-Music:
1. #<MIDI-NOTE | 60| 0.400| 0.500| 0.700| 0|>

2. #<MIDI-NOTE | 58| 0.400| 0.500| 0.700| 0|>

3. #<MIDI-NOTE | 55| 0.400| 0.500| 0.700| 0|>


4. #<MIDI-NOTE | 57| 0.400| 0.500| 0.700| 0|>
5. #<MIDI-NOTE | 59| 0.400| 0.500| 0.700| 0|>
6. #<MIDI-NOTE | 60| 0.400| 0.500| 0.700| 0|>
7. #<MIDI-NOTE | 62| 0.400| 0.500| 0.700| 0|>
8. #<MIDI-NOTE | 60| 0.400| 0.500| 0.700| 0|>
9. #<MIDI-NOTE | 58| 0.400| 0.500| 0.700| 0|>

10. #<MIDI-NOTE | 55| 0.400| 0.500| 0.700| 0|>

15.6 1/f Noise


A special class of noise called 1/f Noise has been discovered in many natural phenomenon
including rainfall patterns and sunspot activity. Oddly enough, the 1/f relationship has been
found by analyzing recordings of non-random music in various styles [Voss, 1978]. Number
series generated using a 1/f formula correlate logarithmically with past values [Dodge, 1986].
Because of this property, 1/f noise seems to have a memory for past values, conceptually similar
to what we've seen with markov processes. This property can be used to realize music that has
highly-correlated attributes.

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

Blue Green Red

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

Example 15.6.1 is a Common LISP implementation of a slightly modified version of the


algorithm by Richard F. Voss. The principle modification is that Example 15.6.1 simulates three
five-sided dice. Each die returns the values 0, 1, 2, 3, or 4. The sum of the three dice ranges from
0 to 12 that easily maps to an octave.

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.

Example 15.6.1: 1-over-f.lisp

(defun 1-over-f (number)


(do* ((counter 0 (incf counter))
(blue (+ 1 (random 5)) (if (= counter 4)
(+ 1 (random 5)) blue))
(green (+ 1 (random 5)) (if (or
(= counter 2)
(= counter 4)
(= counter 6))
(+ 1 (random 5)) green))
(red (+ 1 (random 5)) (+ 1 (random 5)))
(total (+ blue green red) (+ blue green red))
(the-list (cons total ()) (cons total the-list)))
((= counter (- number 1)) (reverse the-list))))

[] 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.

Example 15.6.2: 1-over-f-music.lisp

(generator 1-over-f-music midi-note (start 0 length 10 amplitude .7 channel 0)


(vars (1-over-f-item-stream (make-item-stream 'items 'cycle (1-over-f 10))))
(let ((octave (+ (random 2) 4)))
(setf note (* (item 1-over-f-item-stream) octave)))
(setf rhythm (item (rhythms q q e e q tempo 200)))
(setf duration rhythm))

15.7 Suggested Listening


Gloriette for John Cage for mechanical organ was composed by Heinrich Taube in 1993. The
piece is a tribute to the composer John Cage who died in 1992. As Heinrich Taube states, "In
keeping with the late composer's interest in chance music, this work was composed using an
algorithmic chance process in which the likelihood of the musical notes C-A-G-E being played
gradually increases over the course of the work, the composer's name slowly emerges out of the
harmonic background of G dorian." [Taube, 1993]

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.

Chapter 16: Composing Using Dynamic Musical


Structure
16.1 Dynamically Creating Musical Structure
The Common Music function sprout may be used to create musical structure. The template for sprout is:
(sprout (object))

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.

Example 16.1.1: harmonize.lisp

(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.

Example 16.2.1: encapsulation.lisp

(defun encapsulation (the-name repetition


&optional (start-time 0))
(generator (name the-name) midi-note (start start-time
length 15)
(setf note (* (item (items 46 42 36 57 in heap)) repetition))
(setf amplitude (random 1.0))
(setf rhythm (item (rhythms s s s s. s. s. e e e. e. q. in heap)))
(setf duration rhythm)
(setf channel 0)
(when (> repetition 1)
(sprout (encapsulation nil (decf repetition) time)))))

Stella [Top-Level]: (encapsulation 'test


2)
#<GENERATOR: Test>
Stella [Top-Level]: mix test 0

[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.

Example 16.2.2: sierpinsky.lisp

(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)))))

Stella [Top-Level]: (sierpinski 'main 12 'c2 .5


5)
#<Algorithm: Main>
Stella [Top-Level]: mix main 0

[https://quod.lib.umich.edu/s/spobooks/images/bbv9810/sierpinski.mp3] sierpinski.mp3

16.3 Compositional Environments


Quite often, the output of an algorithm does not result in the creation of an entire composition.
Higher-level compositional environments such as MIDI sequencers or multi-track digital audio
workstations may be used to edit, process, or assemble the output of your algorithms. Post
processing of the output of compositional algorithms implies that these algorithms themselves
are a part of a whole. For this reason, the composer must carefully think about the formal
structure of a composition and how the output of an algorithm relates to the composition as a
whole.

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.

16.4 Suggesting Listening


The 2nd movement of American Miniatures by David A. Jaffe uses a drum pattern derived from
Congolese music, combined with an algorithmic drum improvisation. The latter was done by
systematically performing random perturbations on the drum pattern, with the perturbations
becoming denser and denser, along with an increase in tempo. The output of this Common
Music program was a Music Kit scorefile that was used to drive the Music Kit "mixsounds"
program. Each "note" in the file was an individual drum sample. [Jaffe, 1992]

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]

Appendix A: Using Common Music with MIDI and


the Open Music System
Common Music references a file named cminit.lisp when it is launched. The purpose of
cminit.lisp is to initialize Common Music so that it works with your hardware and software. The
file cminit.lisp should reside in the root level of the Common Music application directory.

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:

(setf *midi-default-connections* '(("input-device" "output-device")))

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:

Registering "Common Music"...


Done.
#<Midi Driver: Open>

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.

Stella [Top-Level]: (in-syntax


:MIDI)
#<Syntax: MIDI>

Appendix B: Using Common Music with Csound


Use the Common Music function in-syntax to use Csound.

STELLA [TOP-LEVEL]: (IN-SYNTAX :CSOUND)


#<SYNTAX: CSOUND>

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))

When Common Music evaluates the code in Example B.1, it responds:

#<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)))

When Common Music evaluates the code in Example B.2, it responds:

#<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.

Stella [Top-Level]: open csound.sco


Stream: #<Csound-Score-File: "csound.sco">
Stella [Top-Level]: mix csound-test
Start time offset:(<cr>=None)
Play file csound.sco? (<cr>=Yes) no

; Common Music output of 3-May-100 14:21:17

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))

Appendix C: Common LISP Symbols, Primitives, and


Commands
-

#| 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

PRINT

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

Appendix D: Common Music Objects, Variables,


Functions, and Commands
(stella) (command)

*cm-state* (variable)

*standard-scale* (variable)

*standard-tempo* (variable)
? (command)

accumulation (item stream pattern type)

algorithm (container)

amplitude (slot)

amplitudes (item stream data type)

analyze (information clause)

archive (command)

average (information clause)

between (function)

channel (slot)

chord (macro function)

close (command)

collect (information clause)

container (object)

count (container initialization)

count (information clause)

count (variable)

crescendo (macro function)

cycle (item stream pattern type)

defobject (macro)

degrees (item stream data type)

delete (command)

diminuendo (macro function)

duplicate (command)
duration (slot)

end (container initialization)

expr (macro function)

expunge (command)

find (information clause)

generator (container)

go (command)

graph (item stream pattern type)

heap (container)

heap (item stream pattern type)

help (command)

highest (information clause)

import (command)

increment (command)

in-syntax (function)

interp (function)

intervals (item stream data type)

invert (command)

item (function)

items (item stream data type)

length (container initialization)

list (command)

load (command)

lowest (information clause)


make-item-stream (function)

map (command)

markov (item stream pattern type)

markov-analyze (function)

maximize (information clause)

merge (container)

midi-note (object)

minimize (information clause)

mirror (macro function)

mix (command)

mute (container)

new (command)

note (object)

note (slot)

notes (item stream data type)

numbers (item stream data type)

object (function)

open (command)

open midi (command)

palindrome (item stream pattern type)

pitches (item stream data type)

random (item stream pattern type)

read-items (function)

repeat (macro function)


rest (object)

retrograde (command)

retrograde (macro function)

rhythm (slot)

rhythms (item stream data type)

rotation (item stream pattern type)

scale (command)

scale/= (predicate function)

scale< (predicate function)

scale<= (predicate function)

scale= (predicate function)

scale> (predicate function)

scale>= (predicate function)

sequence (item stream pattern type)

series (macro function)

set (command)

shuffle (command)

sprout (function)

start (container initialization)

status? (function)

steps (item stream data type)

sum (information clause)

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.

Absolute Referencing - referencing a container by name.

Algorithm - a rule or procedure used to solve a problem.

Algorithmic Composition - the use of computers to implement procedures that result in the
generation of music.

Applicative programming - a technique that allows a function to be applied to each element


of a list.

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.

Cardinality - the number of elements in a set.

Class - a data type that supports inheritance for slots, slot defaults, and methods.

Color - the pitch sequence of an isorhythmic motet.


Common LISP Object System (CLOS) - a set of tools used to develop object-oriented
programs in LISP.

Conditionals - a category of Common LISP forms that choose an action based on an


evaluation.

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.

Dotted List - a cons cell whose CDR points to a non-NIL atom.

Element - an object in a set.

Empty List - a list with no elements or the empty list. May be represented as () or NIL.

Encapsulation - the act of placing one thing inside of another.

Flat List - a list that does not contain any lists as elements.

Floating Point Number - a number that contains a decimal point.

Frozen Generator - a generator whose elements have been computed once and will remain
fixed until the generator is unfrozen and re-computed.

Function - an operation that performs a procedure and returns a result.

Global variable - a variable that exists in the global lexical context.

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.

Integer - a whole number.

Isorhythm - literally means "same rhythm." The compositional practice of mapping a rhythmic
pattern onto a pitch sequence.

Item stream - an ordering of objects based on pattern types.

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.

Iteration - a repeating process.

Keyword argument - a symbol that begins with a colon that modifies the behavior of a
procedure.

Lambda expressions - unnamed or anonymous functions.

Lexical closure - a procedure that creates an environment in which a variable is known.

Lexical context - the region in which a variable is may be referenced.

LISP - a programming language that derives its name from List Processing.

List - a data type that is represented as a chain of cons cells.

Local variable - a variable that exists in a local lexical context such as a LET or LET*.

Macro - a function designed to serve as a group of functions.

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.

Member - an object in a set.

Nested List - a list that contains one or more lists as an element.

Object - an instance of a data type.

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.

Pointer - a pointer gives the address to an object in memory.

Predicate - a function that returns a T or NIL evaluation.

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.

Primitive - a Common LISP built-in function.

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.

Ratio - a fractional number made up of a numerator and a denominator.

Recursion - something that is defined in terms of itself.

Recursive function - a function that calls itself.

Relative Referencing - referencing a container by its position in the object hierarchy.

Scope - the region of a program in which an object may be referenced.

Serial Composition - a method of composition where tonality is undermined by applying a


series to the organization of musical parameters such as pitch and rhythm.

Set - a collection of objects

Slot - a place to store values for a class.

Stella -Common Music's command interpreter.

Stochastic Music - the use of probability theory in the selection of musical parameters.

String - a succession of characters enclosed in double quotes.

Subclass - an object one-level below in a class hierarchy.

Superclass - an object one-level above in a class hierarchy.


Symbol - a LISP data type.

Talea - the rhythmic pattern of an isorhythmic motet.

Tone Row - a sequence of 12 chromatic pitches where no pitch is repeated.

Truth Table - a table representation that shows a T or NIL evaluation when two or more
expressions are combined by a logical operator.

User-Defined Function - a function defined by the user, e.g. not a primitive.

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, Charles. 1987. Automated Composition in Retrospect: 1956-1986. Leonardo 20 (2):169-


186.

Ames. 1989. The Markov Process as a Compositional Model: A Survey and Tutorial. Leonardo 22
(2):175-187.

Ames, Charles. 1995. Thresholds of Confidence: An Analysis of Statistical Methods for


Composition, Part I: Applications. Leonardo Music Journal 5:33-38.

Ames, Charles. 1996. Thresholds of Confidence: An Analysis of Statistical Methods for


Composition, Part 2: Applications. Leonardo Music Journal 6:21-26.

Apel, Willi. 1979. Harvard Dictionary of Music . Cambridge, MA: The Belknap Press of Harvard
University Press.

Assayag, G, C. Rueda, M. Laurson, C. Agon, O. Delerue. 1999. Computer-Assisted Composition


at IRCAM: From Patchwork to Open Music. Computer Music Journal 23 (3):59-72.

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.

Gardner, Martin. 1986. Mathematical Games. Scientific American. (254)16+.

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.

Keene, Sonya E. 1989. Object-Oriented Programming in Common LISP . Reading, MA:


Addison-Wesley 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.

Laurson, M. and J. Duthen. 1989. Patchwork, a graphical language in Pre-Form. Proceedings of


the 1989 International Computer Music Conference. San Francisco, CA: International Computer
Music Association.

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.

Malt, M. 1993. Patchwork Introduction. Paris: IRCAM.

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.

McLuhan, M. 1964. Understanding Media . New York, NY: McGraw-Hill.

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. 1989. Automatic Counterpoint. In Current Directions in Computer Music


Research , edited by M. M. a. J. Pierce. Cambridge, MA: The MIT Press.

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. 1989. Common Music Documentation [html].

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. 1996. Higher-Order Compositional Modeling. Proceedings of the 1996


International Conference on Musical Cognition and Perception. Montreal.

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.

Todd, P.M. 1989. A Connectionist Approach to Algorithmic Composition. Computer Music


Journal 13 (4):27-43.

Tonality Systems. 1993. Symbolic Composer. West Yorkshire: Tonality Systems.

Touretzky, David S. 1990. Common LISP: A Gentle Introduction to Symbolic Computation .


Redwood City, CA: The Benjamin/Cummings Publishing Company, Inc.

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.

Bach, J.S. The Art of the Fugue . ML 5738 Columbia.

Berg, Alban. Lyric Suite . LM-2531 RCA Victor.

Cage, John. HPSCHD . H 71224 Nonesuch.

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.

Dodge, Charles. Profile . 450-73 Neuma Records.

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.

Hiller, Lejaren. The ILLIAC Suite . HS 25053 Heliodor.


Jaffe, David. American Miniatures , mvmt. II-After the Battle of Bull Run. Berkeley, CA: Well-
Tempered Productions.

Kimura, Mari. "U" (The Cormorant). San Francisco, CA: ICMC92: The International Computer
Music Association.

Lansky, Paul. Late August . San Francisco, CA: New Albion Records.

Mozart, Wolfgang Amadeus. Musikalisches Würfelspiel . 422-545-2 Philips.

Simoni, Mary. Eulogy . San Francisco, CA: Leonardo.

Simoni. 1998. Emma Speaks. San Francisco, CA: The International Computer Music
Association. CD-ROM.

Stockhausen, Karlheinz. Klavierstück X . CD 310 009 H1 Koch Schwann Musica Mundi.

Taube, Heinrich. Gloriette for John Cage. San Francisco, CA: ICMC94: The International
Computer Music Association.

Xenakis, Iannis. Amorsima-Morsima. S36560 Angel.

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

Hosted by Michigan Publishing, a division of the University of Michigan Library.


For more information please contact mpub-help@umich.edu.

You might also like