318
This is a pdf of the HTML version of the book Notes from the Metalevel, an Introduction to Computer Composition by Heinrich K. Taube, available at: http://www.moz.ac.at/sem/lehre/lib/cm/Notes%20from%20the%20Metalevel/00/contents.html

Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Embed Size (px)

DESCRIPTION

This a pdf of an html version of the book Notes From the Metalevel: An Introduction to Computer Composition, written by Heinrich Taube and first published in 2005.[It] More infoCommon Music: http://www.musicainformatica.it/risorse/common-music-2.phpIl sito della casa editrice: http://www.routledge.com/books/search/keywords/9026519753/

Citation preview

Page 1: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

This is a pdf of the HTML version of the book Notes from the Metalevel, an Introduction to Computer Composition by Heinrich K. Taube, available at:

http://www.moz.ac.at/sem/lehre/lib/cm/Notes%20from%20the%20Metalevel/00/contents.html

Page 3: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Title Dedication Preface Introduction

Table of Contents

TitleDedicationPreface

1. Introduction2. The Language of Lisp3. Functions and Data4. Etudes, Op. 1: The Lisp Listener5. Lisp Evaluation6. The Art of Lisp Programming

7. Etudes, Op. 2: Musical Math8. Iteration and the Loop Macro9. Etudes, Op. 3: Iteration, Rows and Sets

10. Parameterized Sound Description11. Metalevel Representation

12. Etudes, Op. 4: Score Genereration13. Algorithms and Processes

14. Etudes Op. 5: Steve Reich's Piano Phase15. Microtonality, Tunings and Modes16. Mapping17. Etudes Op. 6: Sonification of Chaos18. Randomness and Chance Composition19. Markov Processes20. Patterns and Composition21. Etudes, Op. 7: Automatic Jazz

22. Etudes, Op. 8: György Ligeti's Désordre23. Spectral Composition24. Beyond MIDI

Index

Title Dedication Preface Introduction

© 2003 Swets Zeitlinger Publishing

Page 4: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Contents

to my children

Konrad Gabriel, Maralena Valentin, and Florian Alexander Taube

There be none of beauty's daughtersWith a magic like thee;

And like music on the waters

Is thy sweet voice to me.

— Lord Byron, Stanzas for Music

Page 5: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

Preface

Notes From the Metalevel is a practical introduction to computer composition. Itis primarily intended for student composers interested in learning how

computation can provide them with a new paradigm for musical composition.Notes from the Metalevel explains through a practical, example-basedapproach the essential concepts and techniques of computer-basedcomposition and demonstrates how these techniques can be integrated intothe composer's own creative work. One of most exciting aspects of computer-based composition is that it is an essentially empirical activity that does notrequire years of formal music theory training to understand. For this reason,Notes from the Metalevel will be of interest to any reader with a high-schoolmathematics background interested in experimenting with music compositionusing MIDI and audio synthesis programs. The book will also be of use tocomputer science and engineering students who are interested in artisticapplications of object-oriented programming techniques and music softwaredesign.

Every composer knows that learning how to write music takes an enormousamount of practice. It is only through practice that new techniques can becomeintegrated into the composer's own approach to the craft. That is why this booktakes a “hands on” approach to its subject matter, by combining a generaltheoretical discussion with real software examples. Notes from the Metalevel isfilled with examples and exercises for the student to perform, study, modify andadapt to their own musical purposes. All of the examples as well as thesoftware to work with them are available on the CD that accompanies the book.The reader is strongly encouraged to experiment with the chapter examplesinteractively, in parallel with reading the text. The vast majority of theseexamples are short, simple, and demonstrate only one or two points at a timeso that the composer can begin to experiment with example code immediatelyafter reading the explanatory text. In addition to the many chapter examples,

Notes from the Metalevel contains a number of Etudes chapters that appear atregular intervals throughout the book. The Etudes are large structured projectsthat explore some particular technique or topic in greater detail.

Plan of the Book

Notes From the Metalevel is divided into two parts. The first nine chapterspresent an introduction to music programming in Lisp. Chapter 1 gives a briefhistory of Lisp and explains why the language is a particularly good choice forsupporting the exploratory process of music composition. Chapters 2-9 give a

selective introduction to computer programming techniques in Lisp. The term

Page 6: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

selective means that the material in these chapters has been selectedspecifically for its relevance to the material presented in the second part of thebook and to musical programming in general.

The second half of the book (chapters 10-24) provides an introduction to theessential concepts and techniques of computer composition. The first fewchapters discuss the representation of sound, musical structure, algorithms andprocesses. The material in the remaining chapters can be broadly grouped intofour areas of discussion: algorithmic design, mapping and transformations,aleatoric composition and pattern based composition.

One of the really wonderful benefits of working in Lisp is that a composerdoes not have to become a Lisp expert in order to do meaningful and importantwork using the language. But some composers want to develop programmingexpertise in parallel with their artistic pursuits. An appendix to the book containspointers to Lisp documentation that is freely available on the Internet. Thismaterial includes several famous Lisp text books, the Lisp and Schemelanguage specifications, and on-line Lisp tutorials. Some of this material is alsoprovided on the accompanying CD. Readers who want to learn more aboutLisp are strongly encouraged to take the additional time necessary to read andstudy this reference material in detail.

Programming Code and LispInteractions

Notes from the Metalevel contains two types of examples: programming codeand Lisp interactions. Program code are Lisp expressions that define units ofcomputation, for example, Lisp procedures or musical algorithms. Programcode is usually saved in text files and then loaded or evaluated in Lisp. Lispinteractions are commands that the composer will input inside the Lispapplication to produce some result, for example, to generate a musical scoresaved as a MIDI file.

All of the programming examples and Lisp interactions for each chapter canbe found on the accompanying CD. The CD provides these examples in twodifferent formats: HTML files and lisp source code files. The HTML files displaythe examples as they are shown in the book together with interactive links to theMIDI and audio files that each example generates. In the hard-copy book theseaudio links are printed with the file name and directory of the example on theCD so that the sound can be easily located.

The Lisp examples source code files contain the examples formatted asplain text. The source code files are suitable for editing in a programming editorsuch as Emacs, or loaded and evaluated inside the Lisp application.

Programming Example Format

Programming examples are printed between dashed upper and lower bordersto indicate that the example should be understood to be part of a larger sourcecode file. Programming code is displayed using syntax highlighting to highlightimportant syntactic units. The hard-copy book and the HTML examples filesdiffer in how this highlighting is depicted. The book displays important syntactic

Page 7: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

units such as definitions, special forms (forms like define and if) andkeywords in bold face and lisp comments in italic face (Example 1).

Example 1. Example program code with syntaxhighlighting.

(define (fibonacci nth) ;; return the nth fibonacci number (if (<= 0 nth 1) 1 (+ (fibonacci (- nth 1)) (fibonacci (- nth 2)))))

The HTML examples files on the CD use text colors for syntax highlighting.Normal code appears in black face, definitions and special forms appear darkred, user comments are blue, text strings are green and keyword symbols(symbols starting with a colon) are red.

Lisp Interactions

Lisp interactions are commands that the user will type inside the Lisp Listener,a window that runs an interactive Lisp command shell. Lisp interactions aredepicted inside a solid box with a light gray background. A single interactionconsists of the Lisp input prompt:

cm>

followed by the user's command expression shown in bold face on the sameline. Results from the command (if any) appears in regular face on the next line.If no output is produced then the next line is left blank. More than one commandmay appear within a single interaction example, in which case the more recentcommands appear toward the bottom of the example. The last line in aninteraction example usually shows the prompt without an input command toindicate that the Lisp Listener is waiting for the user to type the next commandexpression (Interaction 1).

Interaction 1. Example interactions in the Lisp Listener.

cm> (list 1 2 3)(1 2 3)cm> (define 2pi (* 2 pi))

cm> 2pi6.283185307179586cm>

Chapter Source Text

The best way to read Notes from the Metalevel is to run the Common Music

Page 8: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

application in parallel as you study each chapter. This will allow you to test outeach interaction and experiment with the programming examples as youencounter them in the book. The source code for all of the program examplesand interactions in each chapter can be found in a “.cm” file (Common Musicsource file) located in the same directory as the HTML example file for thechapter. Etudes, op. 1 describes two different ways to work with examplematerial. The best way is to edit source examples in a text editor that allowsLisp expressions to be remotely evaluated inside the Common Musicapplication. See Chapter 4 for more information about this topic.

Sound Examples

Every musical score example in the book has one or more sound filesassociated with it that contain the musical output generated by the example.Audio files are stored in stereo MP3 format and MIDI files are stored in Level 0(single track) MIDI format. Except for a few noted cases example MIDI files donot contain program changes so that the reader can experiment with differentinstrumentation when playing back the examples. If no instrumentation issuggested in an example then a Grand Piano sound may be assumed.

Each sound example is marked by an arrow. Examples in the book alsocontain the location of the sound file on the CD. Sound examples in the HTMLexample files are actual links that play the example. A short description of a asound example may appear after its listing:

→ sines.mid Two midi "sine waves" at slightly different rates.→ thunder.mp3 Tuned thunder clap from Aeolian Harp.

Special Notes

Special notes highlight points of difference between Lisp implementations oroperating systems that affect how examples appear or execute on the variousdifferent systems. A special note consists of a styled label and an explanatorytext:

CLTL Note:Common Lisp defines nil to mean both boolean falseand the empty set.

Definitions

Functions and variables defined by Common Music are only briefly explained inthe main chapter text. The full explanation for these entries can be found in theon-line Common Music Dictionary and also located on the CD in the file"doc/dict/index.html" under the Common Music source directory.

Definitions are printed in a special typographic format. Definition terms,literals (code that you type) and program code are printed in fixed width font.Variables and metavariables (names that stand for pieces of code) are printed

Page 9: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

in italic face. Terms separated by | are exclusive choices. Braces {} andbrackets [] are used to associate or group the enclosed terms in a syntaxdeclaration and are never actually written in code. Brackets [] mean that theenclosed terms are optional. An optional term may appear at most one time.Braces {} simply group the enclosed terms. A star * appearing after a bracketmeans that the enclosed terms may be specified zero or more times. A plus +after braces means that the terms may be specified one or more times. Asample syntax definition:

(hertz x [:in scale])

In the sample above hertz is the name of the function, x is a required argument

and scale is an optional keyword argument. Italicized variables in a definition

may have descriptive names or may consist of a single letter such as x. Singleletter names generally provide an indication as to the type of data the user isexpected to specify. The meaning of the letter variables are shown in Table 1.

Table 1. Single letter variable names.

Letter Description Letter Description

x any value b boolean

n number s symbol or keyword

f float l list

i integer g string

Acknowledgments

I would like to thank the Research Board at the University of Illinois for its granttoward the completion of this manuscript. The author is indebted to the Centerfor Computer Research and Acoustics (CCRMA) at Stanford University and tothe Zentrum für Kunst und Medientechnologie (ZKM) in Karlsruhe, Germany fortheir contributions to the Common Music software project on which this book isbased. I would like to thank Johannes Goebel, director of the Audio group whileI was working at ZKM, for his commitment to the Common Music project while Iwas employed at the center. The author would like to acknowledge WilliamSchottstaedt at CCRMA for his contributions to computer music programmingin general and for his Common Lisp Music (CLM) synthesis language which theauthor used to produce the Aeolian Harp examples included on the CD. Theauthor is greatly indebted to Tobias Kunze for his many contributions toCommon Music and for his help in the preparation of this document. Tobiasimplemented many of the MIDI features in Common Music, wrote severalexamples on which Etudes chapters in this book are based, designedCommon Music's glowing lambda logo and developed the CSS style for theHTML version of the book. I am also indebted to Michael Klingbeil for writingseveral programming examples in the book, for allowing me to use excerpts

from his composition 1706°F and also for producing the EPS illustrations thatappear in the hard-copy version of the book. I would also like to express mythanks to Richard Karpen and the Center for Advanced Research Technology

Page 10: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 06 Oct 2003

in the Arts and Humanities (CARTAH) at the University of Washington for theirfriendly encouragement and for the many good improvements and suggestionsthat they have made over the years. And finally, I would like to thank CarlEdwards and David Phillips for their careful reading of this manuscript and fortheir sound editorial advice.

Colophon

This book was written in the Emacs text editor using the ispell spelling programand a lisp-based mark-up language developed by the author. This mark-uplanguage generated the HTML for the book's publication as well as all thechapter example files. HTML output was then imported into Microsoft Word toproduce the camera-ready manuscript. All of the graphic plots that appear inthe book were created in Plotter, a plotting tool included in the Macintosh/OS9.2 version of Common Music. The illustrations were designed in AppleWorks6. A portable version of Plotter that runs in all versions of Common Music iscurrently under development. Both the Mtxt implementation file as well as Plottercode can be found on the CD that accompanies this book.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 11: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

1Introduction

The one crowded space in Father Perry'shouse was his bookshelves. I gradually

came to understand that the marks on the

pages were trapped words. Anyone could

learn to decipher the symbols and turn the

trapped words loose again into speech.

The ink of the print trapped the thoughts;

they could no more get away than a

doomboo could get out of a pit. When thefull realization of what this meant flooded

over me, I experienced the same thrill and

amazement as when I had my first glimpse

of the bright lights of Konakry. I shivered

with the intensity of my desire to learn to dothis wondrous thing myself.

spoken by Prince Modupe, a west Africanprince who learned to read as an adult.

— Leonard Schlain The Alphabet Versus

the Goddess

It is impossible to know exactly how Prince Modupe felt when he discovered aprocess by which his very thoughts could be trapped and released at will againinto speech. But I think his epiphany must be close to what I experienced when,as a young composer, I was first shown how I could use a computer torepresent my musical ideas and then “release them” at will into musicalcompositions. At that time I was a masters student in composition at StanfordUniversity. My teachers John Chowning and Leland Smith had justdemonstrated to our introductory computer music class how a music languagecalled SCORE (Smith) could capture musical ideas and, at the push of abutton, trigger an almost magical process in which fantastically complex scoreswere computed and then realized by instruments unimpeded by the laws ofphysics. At that instant it became clear to me that there was an entire level ofnotation above the scores that I had been writing in my regular compositionclasses, a level I knew nothing about! But I could see that in this level it waspossible to notate my compositional ideas in a precise manner and work withthem in an almost physical way, as “trapped words” that could be unleashed

Page 12: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

into musical sound at the push of a button. Equally important was the realizationthat this new (to me) level was essentially devoid of any preconceived notionsof “musical correctness” and so offered me an attractive alternative to thesymbols and glyphs of Common Practice music, which I increasing felt were tootied to historical tradition. I too, “shivered with a desire to do this wondrous thingmyself” and, as I look back on it now more than twenty years later, I realize thatthis moment was one in which my life's path was irrevocably altered.

The first several years that I was associated with the Center for ComputerResearch in Music and Acoustics (CCRMA) were among the most intellectuallystimulating experiences I have ever had. Our computer music classes werelocated in the D. C. Power Laboratory (named for a person, not a current), anoff-campus facility that was devoted primarily to research in ArtificialIntelligence. The Lab housed a PDP-10 mainframe computer that students inthe computer music group were allowed to use during the night from 2-7am. AsI learned more about music languages during my studies I also took time tolearn about the software languages that AI researchers were using at the Lab. Icannot say with certainty why I did this since I had never taken a class incomputer science and was not particularly good at mathematics! There weretwo primary languages in use: SAIL (Stanford Artificial Intelligence Language)and LISP, a language developed by James McCarthy at Stanford. I wasimmediately attracted to the power and beauty of the Lisp language for many ofthe reasons that are discussed in the next chapter of this book and I began touse it in my musical life.

All things change, particularly in the digital world, and one day the SAMBOXdigital synthesizer that was at the center of my artistic life was retired withoutany immediate replacement. My software world — which was hopelessly tied tothe retired hardware — vanished at the speed of an electron, and all of theprograms and libraries I had used for years were suddenly useless. I decided atthat point that I would develop a generalized and portable replacement to thatworld, one that would run on any machine, and could be customized andredefined even to the core of the language. The result was the Common Musicsoftware system, written in Lisp, that continues to evolve today. Though born ofnecessity, Common Music developed in response to the explosive growth ofnew hardware and music systems that resulted from the introduction of thepersonal computer in the 1980's. As the number of computers, operatingsystems, sound synthesis languages and music applications increased, theneed for a portable composition language that could function with all of thesesystems became obvious. The Common Music software project started in 1989when I was a Rockefeller Fellow at CCRMA. Much of the system as it existstoday was implemented at the Institut für Musik und Akustik at the Zentrum fürKunst und Medientechnologie in Karlsruhe, Germany, where I worked for thefirst half of the 1990's. In 1996 Common Music received First Prize in thecomputer-assisted composition category at the 1er Concours International deLogiciels Musicaux in Bourges, France. Common Music continues to evolvetoday at the University of Illinois at Urbana-Champaign, where the author is nowan associate professor of music composition.

Notes from the Metalevel

Every musician is familiar with performing music from a score. The score is not

Page 13: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

the same thing as the music, it is a representation of music — a notationdesigned to capture information about creating sound with an instrument. Butmusical sound can be represented in a number of different ways and on anumber of different levels. For example, a recording captures music at a verylow level, by storing a representation of the music's acoustic waveform. Ofcourse, the recording does not contain any performance information — musicalnotes and other kinds of performance instructions can be thought of as

constituting a level of abstraction above the sonic waveform.If we compare the score of a composition with its recording there are

several observations that can be made. First, the score is a much morecondensed representation than the recording. By definition, the process ofabstraction eliminates, or hides, a certain amount of detail that exists in thelower, unabstracted layer. In addition to hiding detail, an abstraction can alsobe a more general representation than the information in the unabstractedlayer. Another way of saying this is that, while the abstraction is more concise itmay also be less precise. Imprecision does not mean that something is wrongwith the abstraction, just that there is not an exact correspondence between itand the lower level. For example, given a musical score a performer can createdifferent versions at the acoustic level simply by performing the score a numberof times. In fact, it is the imprecision of traditional scores that enables musicalinterpretation and makes performance art interesting.

A score such as the one Bach is holding in Figure 1 is notated in alanguage that allows a composer to convey information to a performer. This

language provides a set of symbols — the musical marks that appear on thepage — and a syntax, or grammar, for stringing the symbols together to formmeaningful statements. Given the existence of the score representation onemight ask if there is yet another representation that constitutes a level ofabstraction above the performance information? The answer, of course, is yes;it is what this book terms the metalevel. If the score represents the composition

then the metalevel represents the composition of the composition. A metalevelrepresentation of music is concerned with representing the activity, or process,of musical composition as opposed to its artifact, the score. If the metalevelseems more ephemeral than the performance level it may be due to the factthat it is more closely related to the mysterious cognitive processes that occurwithin the composer. In addition, there is no common physical artifact, such asa score or a CD, to represent the metalevel. This book is about using the

computer to instantiate the metalevel: to define, model and represent thecompositional processes, formalism and structures that are articulated in amusical score and acoustic performance but are not literally represented there.By using a computer the composer works with an explicit metalevel notation, orlanguage, that makes the metalevel as tangible as the performance andacoustic levels. But unlike the bits on a CD or the notes on a score, statementsin the metalevel representation are active representations of the processes,methods, algorithms and techniques that a composer develops to craft thesounds in his or her compositions.

Figure 1-1. Music can be represented on different levels ofabstraction, acoustic, performance and metalevel. (The

metalevel is depicted using the Greek lambda

superimposed on a music staff.

Page 14: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Computer Composition

Over the course of the last few decades the computer has played an everincreasing role in the professional lives of composers. Computers are nowinvolved in all phases of a composer's work, from pre-composition explorationto sound generation, score notation and CD recording (Figure 2). Composershave been using the computer as a compositional tool for almost as long as themodern computer has been in existence. Lejaren Hiller and Leonard Isaacson

are generally regarded to be the first “computer composers” and their Illiac

Suite for string quartet was premiered August 9, 1956 at the University ofIllinois at Urbana-Champaign, where the author is on the composition faculty.As computer hardware and software has improved the computer has come totake on an increasing role in music composition. It is now useful to distinguishbetween several different ways that computers can be applied to musiccomposition. In computer assisted composition the computer facilitatescompositional tasks such as computing pre-compositional data, event editing,performing playback and so on. In some sense the computer is applied beforeor after the composer has a compositional idea rather than as a representation

of compositional formalisms.The term automatic composition can be appliedto computer systems that are designed to compose music “independently”.Software such as David Cope's Experiments in Musical Intelligence (Cope)and some types of sound installations are examples of this kind of system.

Computer-based composition means to use the computer to explicitlyrepresent compositional ideas at a level higher than the performance score. Anexplicit metalevel representation means that the relationships and processesthat constitute a composition (the composition of the composition) arerepresented inside the machine apart from the composer thinking about them.

Why would a composer be interested in computer composition? I think themain reason is to think about music and to write music differently than onewould without using a computer. Thus, computer based compositoin affects thecompositional process as well as the composition in a number of differentways:

Computer-based omposition becomes an essentially empirical activity,one in which the composer experiments with ideas and rapidly tests themout as they develop from an initial curiosity into their final compositionalform. Regardless of whether or not a composer writes “experimental

Page 15: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

music”, computer-based composition practically insures that thecomposer writes “experimenting music”.An explicit metalevel representation allows compositional ideas to berepresented as reusable building blocks that may be used in a variety ofmusical contexts and in different musical pieces.Compositional tasks can be solved by applying generic algorithms tosolve specific problems or tasks. These algorithsm can be organized into“toolboxes” that provide services over and over again.Just as it is possible to generate different acoustic representations from asingle score, so it is possible to generate different versions of a scorefrom the same metalevel representation.Complex probability and chance procedures can be incorporated into thecompositional process very easily.Very small changes at the metalevel can have a profound impact on thescore level representation.Relationships that are simple to express in the metalevel can generateextremely complex performance scores.Changes in the metalevel can lead to unintended or unanticipatedconsequences that trigger new compositional ideas or take thecompositional process in a totally new direction. Any composer who hasintegrated computers their compositional process in an experimental waycan cite examples of this phenomena in their own music.An explicit metalevel representation means that compositional ideas canbe manipulated and processed by machines. If the composer canconstruct a metalevel representation, then so can a program. This meansthe metalevel (the composition of the composition) could be an artifact ofother, higher order abstractions in the machine.Metalevel compositions can explore domains too complex or too large toimagine doing without a computer. Of course, since a computer isdeterministic then strictly speaking there is no calculatoin that can beaccomplished with a computer that cannot also be accomplished withpen and paper. But the quantitative difference in speed and processingpower between computer-based and pen-and-paper-based computationis of such a magnitude that for all intents and purposes it is a qualitativedifference for the metalevel composer.

Figure 1-2. The composition abstraction levels

represented in a computer and their relation to the software

systems described in this book.

Page 16: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

References

Smith, L. (1972). Score: A Musician's Approach to Computer Music.. Journal ofthe Audio Engineering Society, 20, 7-14.Cope, D. (1996). Experiments in Musical Intelligence. Madison: A-R Editions.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 17: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

2The Language of Lisp

Just as its hard to describe what makes apiece of music or a painting aesthetically

pleasing, it's equally difficult to describe

what makes a mathematical theorem or a

physical theory beautiful. A beautiful

theory will be simple, compact, and spare;

it will give a sense of completeness and

often an eerie sense of symmetry.

— Charles Seife, Zero, The Biography of aDangerous Idea

What is Lisp?

Lisp is a computer programming language written in Lisp. Behind this tautologylies one of the most unique and interesting facts about the language, namely,that Lisp does not make a distinction between data, the grist of computation,

and programs, the software agents that act on data. A Lisp program is Lispdata. This symmetry between programs and data reflects a very different "worldview" than computer languages like C or Fortran. What are some of theconsequences of Lisp's unique approach to language design that are relevantto the activity of music composition?

Lisp is easy to learn

Lisp's syntax is simple, compact and spare. Only a handful of “rules” areneeded. This is why Lisp is sometimes taught as the first programminglanguage in university-level computer science courses. For the composer itmeans that useful work can begin almost immediately, before the composerunderstands much about the underlying mechanics of Lisp or the art ofprogramming in general. In Lisp one learns by doing and experimenting, just asin music composition.

Lisp is beautiful

Page 18: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

It might seem odd to call a programming language beautiful but many peoplewho use Lisp are attracted to it as much by its elegance as by its efficacy. Likethe M. C. Escher hand that draws itself, the recursive, self-similar nature of Lispcan be inspiring. The symmetry between data and programs gives the wholelanguage a sense of circularity and completeness that makes working in Lisp,for many people, as much an aesthetic experience as it is a practical one. Thisis, I think, an important quality for the novice Lisp composer to be aware of andone that composers are particularly well suited to appreciate. Aesthetics canplay an important role at the compositional metalevel just as it does in theperformance level.

Lisp is interactive and dynamic

By its very nature Lisp supports the exploratory and incremental process ofmusic composition. When a Lisp application runs, a window called the Listeneris available for the composer to interact with the Lisp environment. Thisinteraction allows the composer to rapidly develop new ideas and test them out“on the fly”. Lisp's interactive nature also encourages the composer to startdeveloping a musical program even before fully understanding how it will workor what its final outcome will be. This style of programming is called

prototyping. In prototyping, an application is developed incrementally, through aprocess of experimentation and change.

Lisp is a programmable language

The fact that Lisp is written in Lisp means that the language itself can beprogrammed and extended. Lisp's programmable nature makes it an idealmetalanguage in which to implement other languages and large softwaresystems. The abstraction levels in computer music — composition, synthesisand sound production — can be easily represented by different Lisp software“layers” all of which work together to further a common goal.

Lisp makes symbolic computation possible

Lisp makes it very easy to represent and manipulate compositional structure.All programming languages calculate with numbers but Lisp is designed from

the bottom up to also support symbolic expressions. A symbolic expression isan expression that consists of words and groups of words. Lisp programs canmanipulate symbolic expressions for language processing. Music is a symboliclanguage and the ability to group symbols is fundamental to music composition.The ability to work with symbols and groups of symbols in Lisp means that iteasy for the composer to create and manipulate musical expressions.

Lisp conforms to a language standard

Lisp's language standard insures that all Lisp implementations operate in thesame way manner across all the different computers and operating systemscurrently available. A number of these implementations are free and can be

Page 19: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

downloaded from the internet. This means the Lisp composer can work onpractically every computer available today and at no software expense.

The History of Lisp

Lisp is one of the oldest computer programming languages. It was invented byJohn McCarthy in the 1950's to support research into symbolic computationand artificial intelligence (AI). As a simple demonstration of what symbolicexpressions are and what a Lisp AI program might do with them, consider thefollowing two expressions:

1. Bach is a composer2. All composers are mortal

A program that implements pattern matching and logical inference canconclude a new fact, namely:

3. Bach is a mortal

This is interesting, in part, because if you read the first two sentences (or listento Bach's music!) you wouldn't necessarily conclude that Bach is a mortal. Butby applying generic inference rules to a database of expressions the programcan add new expressions to its knowledge base. In some sense, then, such aprogram has “learned” since the “knowledge” at its disposal has measurablyincreased.

Although research in Artificial Intelligence was one of the initial reasons fordeveloping Lisp, AI has long since ceased to be the central focus of thelanguage. Over the last few decades Lisp has evolved into a general purposeprogramming language that is used for many different types of applications:CAD systems, notation programs, text editors, HTML servers, windowmanagers, even airline reservation systems. But at its core, Lisp remains alanguage that is particularly well suited to research and, by extension, tocreative domains with a strong exploratory nature, like music composition.

Dialects and Implementations

A dialect is a version of a language. As research into symbolic computingprogressed in the 1960s and 1970s a number of different Lisp dialectsemerged. Scheme and Common Lisp (CL) are the two most important Lispdialects in existence today. Each dialect has its own particular strengths:Scheme is a small, elegant, consistent and “pure” version of Lisp; CommonLisp is a much larger, faster and a more “industrial strength” version of Lisp.

Within a single Lisp dialect there are likely to be a number of different

language implementations. An implementation is the computer software thatmakes a specific instance of a language specification. Some Lispimplementations are free, others are products sold by companies. In general,all implementations of a given dialect agree on a common set of features asdefined in its language specification. A language specification is a documentthat defines the exact syntax and semantics of the language. The specification

Page 20: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

for Common Lisp is Common Lisp: the Language (2nd Edition), by Guy Steele.The language specification for Scheme is The Revised (5) Report on theAlgorithmic Language Scheme.

Lisp implementations are allowed add extensions and extra features to thecore language as defined in the language specification, which means thatfeatures not explicitly covered in the specification tend to vary widely fromimplementation to implementation. Common extensions include “developertools” such as compilers, graphic libraries, foreign language interfaces,program code analyzers, optimizers, debuggers and so on.

Common Music and Lisp

The music software system that accompanies this book is called CommonMusic (CM). Common Music produces sound by transforming a metalevelrepresentation of music into a variety of different protocols for controlling soundproduction and display. The system was originally written in Common Lisp butnow includes both Scheme and Common Lisp bindings (Table 1). BecauseScheme is smaller and conceptually more consistent than Common Lisp it isthe dialect that is used and documented in Notes from the Metalevel.

There are several things to keep in mind while working through the book.First, although the book adopts Scheme syntax for pedagogical reasons all of

the examples and program code in Notes from the Metalevel will work with anyimplementation of Common Music that is listed in Table 2-1. Secondly,language extensions provided by the host Lisp implementation are available tothe user when working with Common Music, but Common Music itself does notdepend on them. And lastly, the vast majority of examples in the book do notdepend on any operating system specific features. Where such differences doexist they are documented using one of the special notes discussed in thePreface.

Composers who would like to examine CM's program code to learn how aparticular feature or function was implemented can access the source code onthe accompanying CD. Common Music's Scheme sources have a .scmextensions and Common Lisp sources have a .lisp extension. The sourcesincluded on the CD are a "snapshot" of the software as it was at the time thisbook went to press. Common Music continues to evolve and the reader maywish at some point to install a newer version of the system than the one thatwas shipped on the book's CD. Source code to the most recent version ofCommon Music can always be downloaded via the Internet from the CM projecthome page.

Table 2-1. Lisp Implementations for Common Music

Implementation Dialect Linux OSX MACOS Windows

ACL CL x x x

CLISP CL x x x

CMUCL CL x

Guile Scheme x x x

MCL CL x x

Page 22: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

3Functions and Data

Lisp is a functional language, which means that all actions are accomplishedby functions acting on data. A Lisp function is a program, or procedure, thataccomplishes a task. Most tasks require a function to manipulate some sort ofdata The term data refers to the information that a function acts on, orprocesses. The use of functions in Lisp comes from the Lambda Calculus, abranch of mathematical logic developed by Alonzo Church in the 1930s. TheLambda Calculus defines mathematical computation in terms of anonymous

functions, called lambda expressions, that are applied to input values toproduce output values. This basic computational model is adopted by Lisp butwith two important additions. First, most Lisp functions are not “anonymous”,they have names associated with them. A name makes it easy to refer to afunction and also gives an indication of what purpose the function serves.Secondly, not all functions in Lisp return results. Most Lisp functions behave likelambda expressions — they calculate useful values. But some functions are

used for the effects they produce rather than for a value they return. Forexample, the purpose of the Lisp function define is to add a new definition tothe language rather than to compute a result.

Interaction 3-1. Examples of calling Lisp functions.

cm> (* 2 pi)6.283185307179586cm> (define 2pi (* 2 pi))

cm> (list 2pi 2pi)(6.283185307179586 6.283185307179586)cm>

In Interaction 3-1 the function * returned the product of its inputs 2 and pi. In thenext line of input the function define added a new variable definition to Lisp and

did not return a value. The terms function, lambda body, procedure, specialfunction, macro, and closure are used at various times in this book to refer totypes of Lisp functions. Two of these terms deserve mention here because they

will be used in the next several chapters of this book. A special function is abasic building block of the Lisp language. Special functions are “special”because they have their own unique syntax that provides some essentialfeature of the Lisp language. The define function is one such special function.

Page 23: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

A macro is a function that rewrites expressions. A macro takes an input

expression exactly as the user typed it and then transforms it into a differentexpression which Lisp then executes. This is a subtle but important pointbecause it means that the notation of the external expression may follow its own

special rules of grammar. loop and process are two very important macros inthis book that appear in almost all the music composition programs presentedafter Chapter 9.

Data and Datatypes

The term data refers to the information that a function acts on, or processes.Most functions receive input data, perform a computation, and return an outputvalue back to the caller. In some sense, then, a function can simply be thoughtof as an object that maps input values to output values:

Table 3-1. Functions map inputs to outputs.

Inputs: 1 2 1 2 3 60

\ / \ | / |

Function: + list keynum->hertz

| | |

Outputs: 3 (1 2 3) 261.625

Since we only interact with functions by passing inputs and receiving outputs wedo not need to understand how they are implemented in order to use them. Forthe moment we will just treat them as “black boxes” that provide usefulprogramming services.

One of the most important skills that the beginning music programmer candevelop is an ability to understand what sorts of information will be needed forgiven musical project and how best to represent that information in the actualdata that a Lisp program will process. The composition programs andalgorithms that appear in this book process many different kinds of information,from simple numbers to complex and multifaceted objects in object-orientedprogramming. One of the basic features of the Lisp language is that all data areclassified according to their type. Consider a program that converts a MIDI keynumber to a Hertz frequency value. If key number 60 (Middle C) is the input theprogram would output 261.625 Hertz. While both the input and output are

numbers they are actually different types of numbers. The number 60 is anexact number called an integer. The value 261.625 is an inexact, or real

number. The terms integer, real and number are examples of Lisp datatypesthat classify the data that Lisp programs process. The novice Lisp composer

Page 24: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

must develop an ability to recognize the most common types of data used inLisp programming, to understand how these datatypes can be used torepresent musical information, and to be able to specify this data in proper Lispnotation.

Symbolic Expressions

The Lisp language was invented to support symbolic computation, a type ofprogramming in which tokens, called symbols, can be processed along withquantitative information. A symbol can be thought of as a word. For example,the symbol debussy denotes a great French composer. In symboliccomputation, symbols and groups of symbols are manipulated as if they werewords and sentences. Of course the symbols and symbol groups do not have toreflect a natural language, they might reflect other languages as well, forexample those of mathematics or music. For historical reasons, symbolicexpressions came to be divided into two datatypes that reflect the basicdistinction between words and groups of words. An atom is a basic,

elementary thing like a symbol or a number. A list is group of things. A list canhold atoms and other lists. Lists are the central hallmark of the Lisp language. Infact, the name “Lisp” is an abbreviation for List processing. The following graph

depicts the most common types of symbolic expressions, or s-expressions thatwill be encountered in this book.

symbolic expression / \ atom list / \ number symbol / | \ integer float ratio

The symbolic expressions listed in the graph are actually just a tiny fraction ofthe total number of datatypes represented in Lisp. Lisp also provides themeans to define new types of data and add them into the language.However,the graph contains the most basic and important set of datatypes that the Lispcomposer will work with on a daily basis. The following sections of this chapterintroduce these datatypes, provide examples of their notation and discuss theirpossible uses in music programming.

Integers

An integer is an exact counting number like 1, 0 or -99. Integer digits can beoptionally preceded by a + or - sign. The special prefix #x allows an integer tobe notated in base 16 (hexadecimal), #o is the prefix for octal notation and #bfor binary. For all practical purposes there is no limit on the size of an integer inLisp.

Integers are a basic feature of almost all programming languages. Inaddition to their role in mathematical expressions, many programs use integersfor counting the number of times some action is performed. In terms of purelymusical information, integers are a natural choice to represent pitch classes

Page 25: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

and interval distances expressed as half steps. Integers are also ideal for

representing MIDI information such as key numbers. A key number is aninteger between 0 and 127 that represents the position of a key on a "virtual"musical keyboard. The first key on the keyboard is key number 0; Middle C onthe keyboard is key number 60.

Example 3-1. Samples of integer notation.

44100-11020030123030230244023231332938238160#xFFFF#b-11110000

Real Numbers

A real, or floating point number, is a fractional number like 1.25 or -.45. Verylarge or small real values can be written using exponential notation. In

exponential notation the real number is followed by a suffix e+n or e-n where nis the appropriate power of ten. For example the large real number1230000000000.0 can be written more concisely as 1.23e+12 in exponentialnotation.

Real numbers, like integers, are ubiquitous in programming. But unlikeintegers, there are a few issues to keep in mind when using real numbers. First,a real value is an inexact representation of a quantity. For example, 0.333 isonly an approximation of the quantity 1/3. The value 0.333333 is a closerapproximation to 1/3 than 0.333 but it is still not exact. A real approximation can

only go to a certain precision because there is a limit to the size of floatingpoint numbers in a computer. The maximum precision and size of a realnumber depends on each Lisp implementation. Secondly, when an inexactnumber is used in a calculation a slight error is introduced into the result. If thisresult accumulates over many repetitions of a computation then what wasoriginally an insignificant difference may compound itself into a serious error inthe final result. And finally, mixed arithmetic — mathematical expressions thatcontain both real and rational numbers (integers and ratios) — will alwaysproduce a real result. Multiplying 10 by 1.0 produces 10.0 not 10. Thisconversion into the inexact representation happens automatically and so it maybe the source of confusion for the novice programmer.

Despite these caveats real numbers are a very effective way to representmeasurements, and many types of musical quantities that only need to be“good enough”. These include such things as frequency in Hertz and timevalues in seconds. Values like Hertz and seconds may only need to be goodenough because humans can only discriminate their precision up to a certainpoint. Real numbers between 0.0 and 1.0 can also be used in composition torepresent “normalized” values. A normalized value is like a percentage exceptthat it may be directly scaled by another quantity to produce the desired value.

Example 3-2. Samples of real number notation.

Page 26: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

440.04.3E+2-.2533.333333333333336.5E-10

Ratios

A ratio is a fraction of two integers, for example 1/2 or -3/4. The numerator anddenominator are separated by a / with no spaces. Lisp automatically reducesratios to their simplest canonical form, for example 2/4 becomes 1/2 and 6/3becomes 2.

Ratios are exact fractional quantities. They are useful in music forexpressing exact proportions like those found in mensural notation, theharmonic series or even the Fibbonaci series. For example, if 1 represents awhole note then the ratio 2/3 is the proportion of 2 triplet half notes, 1/5 is 1quintuplet quarter note, 7/8 is 7 eighth notes and so on. Ratios can also be

used to represent intervals in the harmonic series and to produce justly tuned

values by scaling (multiplying) a frequency f in Hertz . For example, 3/2f is thefrequency a just fifth up from f, 8/9f is a just major second down, and so on. Onepotential problem with ratios is that sometimes they can be difficult to interpret.For example, how much is 160929076/151896821f?

Example 3-3. Samples of ratio notation.

13/215/3-3/4

LISP Note:In Common Lisp ratios are an exact representation.Guile converts ratio notation to inexact (real) numbers.

Symbols

A symbol is a sequence of alphanumeric characters, like music or 5th-symphony. Hyphenated symbols are very common in Lisp. Symbols can bethought of as words. Most combinations of letters, digits and hyphens can beincluded in a symbol's name. However, there are a few characters that have aspecial significance in Lisp notation and should not be used in symbol names.These characters are:

# ( ) \ : ; " ' ,

Lisp can be either case sensitive or case insensitive. Case sensitive meansthat a lower case letter is not recognized to be the same as its upper caseequivalent. In a case sensitive Lisp the three symbols machaut, Machaut and

Page 27: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

MACHAUT would all be different symbols. In a case insensitive Lisp the threenotations would refer to the exact same symbol. For simplicity's sake, andbecause standard Lisp programming style uses lower case letters exclusively,this book assumes a case sensitive lisp and symbols are always notated inlower case letters.

Symbols appear everywhere in Lisp. In addition to their role as words andtokens they have a special significance as names for Lisp programs andvariables. A keyword is a special type of symbol that starts with a colon, forexample :retrograde. The colon is not actually part of the symbol's name, it isa special character that defines the symbol to be a keyword. Keywords have aspecial role in programming that will be discussed in the next few chapters. Fornow just remember that keywords can be used as tokens just like regularsymbols can be.

Example 3-4. Samples of symbol notation.

c-sharp-minor+*tempo*sfz:sounds-good

Lists

How data are organized is often just as important as what the data represents!The idea of grouping things together to form larger units of structure permeatesthe activity of music composition and it is also a fundamental property of Lispdata representation. All but the simplest programs in this book exhibit datagrouping that forms higher level units of information out of the constituentelements. For example, a twelve-tone composition project might use integers torepresent the twelve pitch classes and then group pitch classes together toform “rows”. The twelve-tone program would then operate on each row as asingle unit of information. Note that a grouping like a twelve-tone row ishomogeneous — all of its elements are of the same type and represent thesame type of information. But this need not be the case — grouping can reflectan association between complex and heterogeneous information as well. Forexample, a project that involves Forte sets might represent each set as a groupof three elements: the name of the set, the prime form of the set and the intervalvector of the set. Each of these values means something quite different in therepresentation and would likely be implemented using different Lisp datatypes.To understand more about how data can be grouped into bundles to form“higher level” representations requires some knowledge about how Lisp itselfrepresents and organizes data.

A Lisp list is a container. The contents of a list are called its elements. Anykind of data, including lists, can be placed inside of list. The ability to organizedifferent things inside of lists makes them an extremely versatile tool and theirrecursive nature means that there is practically no limit to the depth andcomplexity of information that they can represent. A few very simple formats fororganizing list elements have proven to be especially effective for representing

Page 28: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

many different types of information. The most basic of these formats is calledthe property list. The property list format will be discussed in a later section ofthis chapter.

It is not possible to categorize all the ways in which lists can be used inmusic composition — sets, rows, notes, chords, measures, phrases, sections,movements, pieces, staves, sequences, matrices, envelopes — the list goeson and on. But before learning how musical information might be representedwith lists it is necessary to understand how a list is notated.

List Notation

Lists are beautiful. They are simple, powerful, flexible and extremely easy towork with. There are only four very simple rules to proper list notation:

1. The starting and ending points of a list are marked by parentheses: ().The left, or open, parentheses ( marks the start of the list and the right, orclose, parenthesis ) marks its end.

2. Every open parenthesis must be matched, or balanced, by exactly oneclose parenthesis.

3. A close parenthesis matches the nearest unmatched open parenthesis toits left.

4. Spaces, tabs or lines separate elements in a list.

It is very important to realize that the ability to understand and implement Lispmusic programs depends on the ability to read and write lists. So if youunderstand the preceding rules then you are ready to start programming!

Example 3-5. Samples of list notation.

()(0 3 7 11 2 5 9 1 4 6 8 10)(how many violists does it take to screw in a light bulb?)(3-7 (0 2 5) (0 1 1 0 1 0))(staff violin-1 (clef treble) (signature c-minor) (meter 2 4) (measure (rest 8th) (note g4 8th ff) (note g4 8th) (note g4 8th)) (measure (note e4 half fermata)))

The first list in the example has no elements. A list with no elements is calledthe empty list. In other words, the empty list is still a list — a list without elementsis not the same as no list at all. The second list in the example has 12 elements,each element is an integer. This list is one possible way to represent the twelvetone row from Berg's Violin Concerto. The third list demonstrates howsentences can be represented by symbols grouped into lists. The fourth listdemonstrates one possible representation of a Forte set. It includes the name,

Page 29: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

prime form and interval vector of [025]. The last list in the example shows onepossible way to represent the opening violin part of a very famous symphony.

Property lists

Lists are collections that associate or group information together. Although listscan reflect a multitude of different organizations there are a few list formats thathave proven to be very effective for representing different types of information.The most important of these list formats is called the property list. A property listis a list whose elements are interpreted pair wise. The first element of each pairis called the property and the second element is called the value. Since a pairconsists of two elements a property list will always have an even number ofelements. The entire list can be interpreted as the description of the properties,or attributes of some entity. How might this be used to represent music?Consider a program that computes flute compositions. Each note for thiscomposition might be defined to have the acoustic properties of duration,frequency, amplitude, and timbre. Given this set of properties, individual notesin a composition would be specified by computing the appropriate values foreach property of each note. For example the program might start out thecomposition by computing the following note description:

Table 3-2. Property and value pairs.

Property Value

duration 7/8

frequency 440

amplitude ff

timbre breathy

The information in this table is easily represented in the composition programas a property list:

Example 3-6. Property list notation.

(duration 7/8 frequency 440 amplitude ff timbre breathy)

It is quite common for keywords (symbols that start with ":") to be used as theproperty names in lists such as the one shown above. There are severalreasons for this. First, using keyword names gives a visual cue as to what isserving as name and what is value in a property list:

Example 3-7. Keyword names in property lists.

(:duration 7/8 :frequency 440 :amplitude ff :timbre breathy)

Page 30: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Secondly, some functions we will encounter later in this book allow keyword

arguments to be passed to the function. A keyword argument is an optionalinput value that is referred by its keyword name. The syntax for these argumentsshares the same keyword and value pairing as seen in the list above.

Function call lists

Passing inputs to a function and receiving its output is referred to as calling a

function, or applying a function to its arguments. Function calls follow a veryconsistent format in Lisp: a function call is a list whose first element is the name

of the function and the rest of the elements (if any) are arguments to thefunction. Arguments are the data passed to the function as input.

This type of notation — in which the function always appears before the datapassed to it — is called prefix notation. Prefix notation makes it easy todetermine when symbols are used as functions and when they are not: for asymbol to act as a function it must always be in the first position of a list. InExample 3-8, the symbols * list and expt are functions because each is in thefirst position of a list. The symbol pi is not used as a function because it is thenot the first element of a list.

Example 3-8. Function call notation.

(* 2 pi)(list 2 pi)(* 440 (expt 2 1/12))

Note that function calls may be nested. In the third example the secondargument to the * is itself a function call. Embedding function calls inside otherfunction calls is very typical of Lisp programming style.

Other Common Datatypes

This chapter has introduced the basic building blocks of symbolic expressions— numbers, symbols and lists — that are used by the composition programs inthis book. There are two other datatypes that historically were not considered tobe symbolic expressions but nevertheless appear in many of the programmingexamples in this book.

Boolean

Both the integer and real datatypes represent an infinite number of values. Theboolean datatype represents only two possible values: true and false.

Computer programs use boolean values to express conditional actions,actions that are only performed if a certain test is either true or false. Forexample, a music composition program might start by first randomly generatinginterval sets until a set with a certain interval vector content has beengenerated. If that test is true then the program begins to compose using the set,

Page 31: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

otherwise (the test was false) the program keeps picking random interval setsuntil it finds one that it "likes". Logic tells us that if something is not false then itmust be true. In other words, false is false and true can be expressed as "notfalse". Some computer programs in Lisp do not test for an explicit boolean truevalue, they simply check to see that values are not boolean false. This allows

predicate functions, functions that test the truth or falsity of an input expression,to return values other than explicit boolean true and false values.

Example 3-9. Samples of boolean notation.

#f#t

LISP Note:Scheme defines #t as boolean true and #f as booleanfalse. Common Lisp defines the symbol t to mean trueand the symbol nil to mean false. This book generallyrefers to these values as simply true or false.

Text Strings

A string is a sequence of text characters delimited by double quotes at the startand end of the string, for example "Music" or "Das Lied von der Erde".Strings are used primarily for printing messages to windows or printing text tofiles stored on the computer. They are also used to specify file names and filedirectories on the computer. Strings are simply blocks of text, they have noother significance to Lisp. The text within the string may contain anycombination of characters, even the special characters listed in the sectiondescribing symbols. To include the " character as part of the text it must be

preceded by the special escape character \ which tells lisp that the quotefollowing the escape character is not interpreted as the end of the string.

Do not confuse strings with symbols or lists. "Music" is word-like but the pairof double-quotes makes it a string not a symbol. "Music is an art" containswords separated by spaces but it is not a list. These differences can be seen inthe following summary table:

Table 3-3. Strings, symbols and lists.

symbol: music

text string: "music"

list of symbols:(music is an art)

text string: "music is an art"

text string: "(music is an art)"

list of strings: ("music" "is" "an" "art")

Page 33: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

4Etudes, Op. 1: The Lisp Listener

Etudes Op. 1 introduces the Common Music application and the Lisp Listener.This chapter assumes that you have copied the contents of the CD to yourcomputer's hard drive. The exercises discussed below represent a “barebones” method for working with Lisp and Common Music. Once you haveperformed the Etudes in this chapter you will certainly want to download thelatest version of Common Music fromhttp://sourceforge.net/projects/commonmusic/ and begin working with Lisp andCommon Music from inside the Emacs or Xemacs text editors. These editors“understand” Lisp syntax and will allow you to work with Lisp interactively, asyou edit and develop your program source code. Both Emacs and Xemacs arefree and run on all modern computers and operating systems. The reader is

strongly encouraged to download either of these editors and learn how to useit. However, this subject matter is beyond the scope of this book. Pleaseconsult the last section in this chapter for more information on working withEmacs or Xemacs.

Op. 1, No. 1: Starting the Common MusicApplication

To start the Common Music application first locate the directory that containsthe contents copied from the CD. This directory will always be referred to as the“NM project directory”. Once this directory has been located the CommonMusic application can be started by performing one of the instructions listedbelow that matches your computer's operating system.

MACOS Note:Double-click the Common Music application iconlocated in the NM project directory.

OSX Linux Note:Open a Terminal window and execute the 'cm.sh' scriptin the bin/ subdirectory of the NM project directory. Thisexample assumes NM project directory was restoredunder the author's home directory:

/User/hkt/ ~/NM/bin/cm.sh

Page 34: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Windows Note:Double-click the file 'xcm.bat' located in the NM binproject directory.

Once the application launches a Lisp Listener window will be visible on thescreen (Figure 1). Note that the appearance of window components such asborders, title bars, scroll bars and cursors will be different on each operatingsystem.

Op. 1, No. 2: The Lisp Interpreter

The main pane in the Listener window is called the Lisp interpreter. Theinterpreter allows the user to interact directly with Lisp by typing inputexpressions into the window and then observing what Lisp returns as an outputvalue on the next line. You might think of the interpreter as a fancy calculator thatdetermines the value of any Lisp expression you type to it. Whenever theinterpreter is waiting for the user to enter an expression it displays the promptcm> and the cursor bar just after it marks the insertion point, the position in thewindow where the next input expression should be typed. The prompt cm> is anabbreviation for Common Music, the Lisp application that is running inside theListener. In this book the user's input expressions are always printed in boldface to distinguish them from the output values that Lisp prints back to thewindow. An output value always appears on the line directly beneath the inputexpression that produced it.

Click on the Listener window to make sure it is active and then type the

following expression at the insertion point at the very end of the last prompt linein the window. Then press the Return key to enter the expression into Lisp. TheReturn key is not displayed in the examples, it is assumed that the user pressesReturn at the end of every input expression.

Interaction 4-1. Evaluating numbers.

cm> 440440cm> 44100.044100.0cm>

As the user works inside the interpreter the entire interaction history is visible inthe window but only the insertion point at the very end of the last line is active fora new line of input. Try entering a few different types of numbers and symbolsinto the interpreter:

Interaction 4-2. Evaluating symbols.

cm> pi3.141592653589793cm> *scale*

Page 35: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

#<tuning "standard-chromatic-scale">cm> stravinsky*** - EVAL: variable STRAVINISKY has no value1. Break [4]> abortcm>

Entering the first two symbols caused each variable's value to be printed(variables are discussed in the next chapter). Entering the fourth symbol

stravinsky triggered a Lisp error.

Op. 1, No. 3: Lisp Errors

Whenever lisp encounters a problem it prints an error message so that the usercan address the problem. Each Lisp implementation has its own method forhandling errors. Some versions of Lisp (for example Guile) may immediatelyreturn the user back to the Listener prompt after the error message is printed.

Other implementations may enter an error break that allows a programmer to“inspect” the Lisp environment in order to determine the cause of the error.Error breaks are indicated by the Lisp prompt changing to a special “breakprompt” after the error message has been printed. Break prompts usually looka bit different than the normal top-level Lisp prompt and often include a numberthat shows the current error level the user is working in. The error levelindicates how many errors must be resolved before Lisp is back in its normalworking environment.

Lisp errors should never cause the application to “crash” or the computer tofreeze. An error is really just a kind of report that attempts to explain what waswrong with the expression that caused the error. Note that while Lisp may beable to determine exactly what went wrong with an expression it cannotdetermine why something went wrong. This is because Lisp cannot know all theconditions that led to the problem. For example, when an unbound variableoccurs in the Listener it may be due to the user misspelling the name of anactual variable or it may be the correct name of a variable the user forgot todefine. Because Lisp cannot know the intentions of a user, error messagesmay seem cryptic at times, or may not give helpful information for correcting theproblem. The novice Lisp composer should prepare for this eventuality bydeveloping a consistent strategy for determining what caused the error and

where that code is located in a program. This is called debugging a problem.Here are a few simple strategies to follow:

Read the error message carefully and completely.Attempt to determine exactly what symbols or expressions are involved inthe error.Locate these expression in the code you were just using.Study the code and its surroundings to see if you can identify the problem.If you cannot identity the problem, reduce the expression to smallerpieces until you have isolated the exact source of the problem.

There is practically no limit to the type and number of errors a human can make!The good news is that most problems are easy to resolve because they aresimple mistakes like mistyping a name. The novice Lisp composer should pay

Page 36: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

particular attention symbol spelling and proper list formatting. Here is a list ofthe most common errors that the novice Lisp composer may make:

Misspelled symbolImproperly formatted listImproper function call syntaxFunction call list whose first element is not a functionSymbol or list not quoted

Once you have determined what the error is you should always resolve, orabort, the error before continuing to input Lisp expressions in the Listener.Each implementation of Lisp has its own mechanism for resolving errors:

OSX Linux Windows Note:Type the symbol abort to the break prompt (Interaction4-2).

MACOS Note:Press the Apple command key and then type the “.”(Command-.) or select the Abort Error item in the Lispmenu.

Lisp evaluation and error are discussed in more detail in Chapter 5.

Op. 1, No. 4: The Lisp Editor

A composer spends most of the time in Lisp developing music programs. Thismeans that a large portion of your time will be devoted to writing and editingyour source code in a text editor. Emacs (and Xemacs) is without a doubt thebest editor to use when you work with Lisp code and Common Music. BothEmacs and Xemacs are mouse-and-menu driven graphical editors that“understand” Lisp syntax and can even run the Common Music applicationinside a special edit window. This tight integration between Emacs and theCommon Music application will allow you to edit your Lisp source code andthen remotely evaluate Lisp expressions in your Emacs edit window as youdevelop your program. For more information on installing and using Emacs orXemacs with Common Music, please consult the Common Musicdocumentation on Emacs.

Example 4-1 shows what a Lisp file looks like inside the Emacs editor. Ifyou are looking at the HTML example page, notice that different parts of thecode are colored according to what type of syntactic unit it is: comments (linesbeginning with one or more semi-colon characters ;), definitions, special forms

and keywords. Emacs calls this colorized display syntax highlighting. If you donot see different colors, select “Syntax Highlighting” from the Options menu toturn highlighting on. Note that in the book these syntax highlighting will bedisplayed in black and white.

To edit the example file from inside Emacs, choose the “Open File” menuitem from the File menu. When the dialog window pops up locate the file "opus-1.lisp" in the examples subdirectory of the NM project directory and select thefile. You can navigate through the list of files in the Open File dialog using the

Page 37: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Up and down arrows. Press the Return key to select a file or to enter ahighlighted directory. Once you have selected the file it will be displayed in aneditor window. If you have installed Emacs according to the Common Musicdocumentation you can select the “Lisp Listener” item in the Lisp menu to startCommon Music running in its own Emacs window. Then examine the file andperform the suggested actions. Here is the contents of the example file(Example 4-1). If you do not have Emacs installed, you can type eachexpression (skipping the comment lines beginning with ;) directly to the Lispinterpreter.

Example 4-1. A Lisp file

;;; This is a sample lisp source file and you are ;;; reading a Lisp comment. A comment begins with ;;;; and continues to the end of the line. Lisp ;;; ignores comments when it process source code. ;;;;;; To evaluate a lisp expression inside Emacs,;;; first select the expression by double-clicking it;;; and then use the Eval Expression command in the;;; Lisp menu. Try evaluating the following expressions;;; in the manner just described. The results from each;;; evaluation will appear in the Listener window.

2pi1/4

;;; To evaluate a list double-click one of the outer;;; parentheses to select the whole expression. Then;;; use Eval Selection to evaluate it.;;; You can also evaluate expressions by placing the;;; cursor on the start or just after the expression ;;; and then typing: C-x C-e (control-x control-e).

(* 2 pi )(list 2 pi)(sin (* 2 pi ))(define albans-row '(0 3 7 11 2 5 9 1 4 6 8 10))(define (row->matrix row) ;; this function is discussed in Chapter 8 (loop for i in row collect (loop for j in row collect (mod (+ (- i) j) 12))))(pprint (row->matrix albans-row))

Op. 1, No. 5: Improvisation

Try typing a few expressions of your own inside the text editor's window andthen evaluating them. Once you have finished experimenting with the samplecode you can quit Common Music by selection “Quit” from the File menu in theListener window.

Page 38: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file cmapp.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 39: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

5Lisp Evaluation

In this chapter we examine the process by which Lisp evaluates, or interprets,Lisp expressions in order to produce results. Since Lisp programs are built outof Lisp expressions, understanding this evaluation process is key tounderstanding Lisp notation and the behavior of the music programs we willdevelop over the course of this book. In a very real sense the process ofevaluation defines how Lisp operates as a programming language.

The Interpreter

In Chapter 4 we learned how to interact with Lisp by typing input expressionsinto the Listener window and then observing the results that Lisp printed back tothe window. The Listener window supports this interaction using a program

called the interpreter, or read-eval-print loop (Figure 5-1).

Figure 5-1. The Lisp interpreter continuously processes

input in its read-eval-print loop.

The terms read, eval and print define three different stages in the processing ofLisp input expressions. (These stages are literally implemented by threeseparate Lisp functions called read, eval and print.) At the beginning of eachcycle through the loop the interpreter waits for the user to input an expression.Once a user enters some text — either by typing it and pressing the Return keyor by remotely evaluating a block of text in an Emacs buffer — the interpreter

Page 40: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

processes the input by invoking the three steps of the read-eval-print loop insuccessive order. We now examine each of these steps in more detail.

Read

In the read step the text string that the user inputs into the Listener window isfirst parsed from the window and then assembled into a true Lisp expression. Ifa valid Lisp expression results then the interpreter goes on to the next step(eval) in the process. If a Lisp object cannot be parsed from the input then theinterpreter signals an error and waits for the user to correct the problem.

Eval

During the eval step the interpreter determines the value, or result, of an inputLisp expression. This evaluation process lies at the very heart of the language.Unfortunately, the evaluation process is not actually visible in the Listenerwindow — it happens “behind the scenes”, before any output is printed back forthe user to see. This may make the effects of evaluation seem a bit mysteriousto the novice Lisp composer. But the mechanism is simple to understand if therules that govern its behavior are understood. These evaluation rules arecovered in the next few sections of this chapter.

Print

In the print step the interpreter displays the results of evaluation back to theListener window. The print step is in some sense the “reverse” of the read step— the Lisp expression that results from the eval step is translated into a textstring and then displayed on a new line in the window. Once this output hasbeen printed the interpreter starts the read-eval-print loop all over again bydisplaying the input prompt and waiting for the user to enter the next commandstring.

Lisp Evaluation

The “goal” of Lisp is to determine the value of Lisp expressions. Evaluation isaccomplished by applying a small number of “interpretation rules” to an inputexpression. The applicability of these rules is determined by the datatype of theinput expression. This means that the easiest way to learn about the evaluationprocess is to observe its effects on the basic datatypes that were presented inChapter 2.

The Evaluation of Numbers, Booleans andStrings

Numbers, booleans and strings are self-evaluating expressions. A self-evaluating expression means that the expression does not change as a result

Page 41: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

of evaluation. Common sense tells us that numbers and booleans must be self-evaluating because, if they were not, the laws of mathematics and logic wouldmake no sense — zero, one, true and false must remain ever so! Strings areself-evaluating expressions because Lisp simply declares them to be so.

Note that — although self-evaluating expressions remain unchanged by theevaluation process — their “appearance” in the print step may be different thanin the read step. This difference does not mean that the value has actuallychanged, but only that the print step has notated the value differently than howthe user first entered it.

Interaction 5-1. Some self-evaluating expressions.

cm> 4410044100cm> #xFFFF65535cm> .5000.5cm> "Ondes Martenot""Ondes Martenot"cm>

We can summarize our observations about self-evaluating expressions in asimple rule to remember:

Lisp Evaluation Rule 1:A number, boolean or string evaluates to itself.

Symbol Evaluation and Variables

In Chapter 2 we learned that symbols can be treated as words and groupedinto lists to form symbolic expressions. But here are two symbolic expressionsthat use the symbol pi differently:

(pi is a greek letter)(* 2 pi)

The first expression treats the symbol pi as the name of a Greek letter. But the

second expression appears to represent an action: "multiply 2 and pi together."In this case the symbol pi would clearly be standing for a particular numericalvalue: 3.141592653589793.

A symbol that stands for a value is called a variable. It is important tounderstand that a symbol and a variable are not exactly the same thing. A

symbol names a variable. A symbol is created just by typing its name but avariable can only be created by defining it using special mechanisms providedby Lisp. Although Lisp symbols appear to be just words they are actuallycomplex objects that possess a number of different properties. One of the

properties of a symbol is it's print name, the text that you see printed on thescreen. A symbol also has a value cell property. This value cell allows a value

Page 42: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(any Lisp expression) to be associated with the symbol. This association is

called a variable binding. It is only through the eval step that symbols can betreated as variables. When the eval step encounters a bound symbol its value isreturned instead of the symbol. If the symbol is not bound before it is evaluatedthen Lisp will signal an “unbound variable” error. One of the interesting featuresof Lisp that distinguishes it from many other programming languages is the factthat a symbol's value cell can hold any Lisp value. Most programminglanguages constrain variables to hold only one particular type of data. But inLisp it is the data itself that is typed, or classified, rather than the variable. Thismakes it simple to work with variables in Lisp because the composer does nothave to “declare” a variable's type or insure that the variable is set to only onespecific type of data.

Interaction 5-2. Symbols are evaluated as variables.

cm> pi3.141592653589793cm> messiaen> Error: Unbound variable: messiaen Abortedcm>

In Interaction 5-2 the result of evaluating pi produced a value because thesymbol pi was defined to represent that value. The evaluation of the symbolmessiaen signaled an error because this symbol was not defined to have avalue before it was evaluated. We summarize symbols used as variables in asecond rule of evaluation:

Lisp Evaluation Rule 2:A symbol evaluates to its variable value. Lisp

signals an “unbound variable” error if the

variable does not have a value.

Evaluation of Function Call Lists

A function call is the application of a Lisp program to input data. Function callsare notated as lists: the first element of the list is the function and the rest of theelements are called the arguments to the function. In the expression:

(* 2 pi)

the symbol * is the function and 2 pi are its two arguments. The evaluation of afunction call requires that each argument to the function be evaluated before thefunction is actually called. This process can be demonstrated using theexample above. Before the multiplication can occur the two arguments 2 and piare first evaluated in left to right order to produce the values that are to bemultiplied. The first argument 2 is a number and therefore evaluates accordingto our Rule 1 to produce the value 2. The second argument pi is a symbol andso it is evaluated as a variable, according to Rule 2. The application of the

Page 43: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

second rule results in the value 3.141592653589793. Since neither argumentproduced an error, their values are passed to the function *, which multipliesthem together and produces the result 6.283185307179586 that is is thenreturned as the value of the function call.

Since each argument in a function call is evaluated before the function iscalled, arguments may include variables and other function calls. It is very

common to see nested function calls in lisp programming. A nested functioncall is an argument to a function that is itself a function call. In the last inputexpression in Interaction 5-3 the function call (* 2 pi 1/4) is nested as theargument to the sin function.

Interaction 5-3. Function call notation.

cm> (* 2 pi)6.283185307179586cm> (list 2 pi)(2 3.141592653589793)cm> (sin (* 2 pi 1/4))1.0cm>

Each of the input lists in Interaction 5-3 are function calls. Recall that the firstelement in a function call is a function and the rest of the elements arearguments to the function. This means that in the expressions above thesymbols *, list and sin are treated as functions because they are symbols inthe first position of a list. The symbol pi is not a function because it is not thefirst element of a list. Lisp signals an error if the first element is not a function ofif any argument cannot be evaluated. Arguments in a function call are evaluatedin left to right order to produce input values to the function. The inputs arepassed to the function and the result returned by the function becomes the valueof the function call expression. We summarize function call lists in a thirdevaluation rule:

Lisp Evaluation Rule 3:

A Lisp list is evaluated as a function call. Lisp

signals an “undefined function” error if the firstelement is not a function.

Blocking Evaluation

The previous two sections have described how the eval step processessymbols and lists. This brings up the obvious question: how can symbols betokens or lists be data structures if the evaluator always treats them asprogramming variables and function calls? For example, how can pi ever beused as a word or a list used to represent a 12-tone row? The answer to boththese questions is that the eval step provides a simple mechanism that allowsus to selectively “turn off” the evaluation of any input expression such that theexpression becomes data, with no special interpretation made by the eval step.

Page 44: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The Lisp Quote

The single quote character: ' is a very special prefix character that “defeats”evaluation, by forcing Lisp to accept the expression that appears just after thequote exactly as the user typed it. Another way of saying this is that the evalstep evaluates a quoted expression by stripping off the quote and returning the(now unquoted) expression as the value. The quote character can be applied toany Lisp expression. In the case of numbers, booleans and strings the quotewill have no effect since these are self-evaluating anyway. But a quote in front ofa symbol causes Lisp to “see” just the symbol itself rather than any variablevalue it may have. A quote in front of a list of numbers allows that list torepresent a grouping of data, for example, a twelve-tone row. A quote before afunction call will cause it to become just a list of data rather than a function call.

Interaction 5-4. The effect of quote on evaluated

expressions.

cm> '4410044100cm> '"Ondes Martenot""Ondes Martenot"cm> pi3.141592653589793cm> 'pipicm> ''pi'picm> '(0 2 5)(0 2 5)cm> (* 2 pi)6.283185307179586cm> '(* 2 pi)(* 2 pi)cm>

Lisp Evaluation Rule 4:

A quoted expression evaluates to the expression

without the quote.

To quote or not to quote...

It is easy for a novice Lisp composer to become confused about when andwhen not to use quote in the Lisp Listener. Although no set of rules can cover allpossible situations there are a few simple guidelines that should help make thisdetermination less confusing:

Numbers never need to be quoted.Booleans (true and false) never need to be quoted.Strings never need to be quoted.Symbols need to be quoted unless they have been defined as variables

Page 45: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

or they are keywords (symbols starting with the colon character)Lists need to be quoted unless they are function calls.

The Lisp backquote

The Lisp backquote character: is related to the quote character. But rather

than block evaluation altogether, the backquote performs selective evaluation.Backquote is used primarily to specify lists in which some elements should be

evaluated and others not. A backquoted list acts as a template in whichelements in which each element remains constant unless it preceded by acomma, in which case it will be replaced by its evaluated value (Interaction 5-5).

Interaction 5-5. The effects of quote and backquote on

lists.

cm> '(a b pi c)(a b pi c)cm> (a b ,pi c)(a b 3.141592653589793 c)cm>

The first input expression in Interaction 5-5 is a quoted list and so Lisp returnsthe entire list exactly as it was typed as the evaluated result. The second inputexpression is a backquoted list that selectively evaluates the variable pi andreplaces it with its evaluated result in the list. A backquoted list acts just like aquoted list except that expressions preceded by a comma “,” are explicitlyevaluated. The backquote is particularly useful for notating lists that will containmany unevaluated tokens and a few expressions that must be evaluated. Sinceeach unevaluated expression would otherwise have to be quoted in anevaluated list, the backquote can substantially reduce the size and complexityof a notated expression by eliminating the need for explicit quotes. Thefollowing two expressions are equivalent:

Interaction 5-6. Equivalent expressions using list and

backquote

cm> (list 'a 'b pi 'c)(a b 3.141592653589793 c)cm> (a b ,pi c)(a b 3.141592653589793 c)cm>

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file eval.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside the

Page 47: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

6The Art of Lisp Programming

The process of preparing programs for adigital computer is especially attractive,

not only because it can be economically

and scientifically rewarding, but also

because it can be an aesthetic experience

much like composing poetry or music.

— Donald E. Knuth

A program in Lisp is a sequence of Lisp expressions that — when evaluated —produce an intended result. Once a programming task is understood,implementing the code really consists of only three things: defining variablesthat hold data, defining functions that operate on data, and calling the functionsto produce the results. The clear, consistent, and functional nature of Lispmakes this process easy to learn. The following little music program should bereadily understandable by the reader even before the specific content can beexplained. (Looping is discussed in Chapter 7.)

Example 6-1. A small Lisp program.

(define middle-c 60)

(define albans-row '(0 3 7 11 2 5 9 1 4 6 8 10))

(define (transpose-row row offset) (loop for pc in row collect (+ pc offset)))

After studying the example, start the Common Music application and evaluateeach expression in the Listener to see what the function transpose-row returns.Try calling the program several times, as demonstrated in Interaction 6-1.

Interaction 6-1. Using the program

cm> (transpose-row albans-row middle-c)(60 63 67 71 62 65 69 61 64 66 68 70)cm> (transpose-row '(0 2 5) 90)(90 92 95)cm> (transpose-row albans-row (random 80))

Page 48: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(17 20 24 28 19 22 26 18 21 23 25 27)cm>

Programs are Functions

Because all actions in Lisp are accomplished by calling functions, learning howto write music in Lisp amounts to learning how to call functions and knowingwhich functions to call in a given situation. A large part of this learning processis to become familiar with the most frequently used functions encountered inmusic programming. To “become familiar” with a function means to learn itsname, to know what results it returns, and to remember the number and type ofinput values the function accepts in a function call. . This chapter introduces acore set of Lisp functions that the composition programs presented in the restof the book depend on. This core set includes the basic arithmetic and booleanoperators, list accessors, and the special function define that adds newvariables and functions to Lisp. The next chapter presents several small andsimple music programs that use some of the functions defined in this chapter.

The core set of functions presented here are actually only a very smallportion of the total number of functions available to the Lisp programmer. Thereader is strongly encouraged to consult the language specifications forScheme or Common Lisp to learn about the complete set of functions availablefor use. The language specifications for Scheme and Common Lisp areindispensable tools for beginning and advanced Lisp users. One way to quicklygain familiarity with Lisp is to examine all of the entries that are related to thefunction definition you look up in the specification. These related functions willbe found in the same section of the specification as the definition that youlooked up.

Function Names and Function Types

The name of a function will usually give a good indication of what its purpose is.For example, the function named + calculates the sum of its input values. Lisp

programmers have developed an informal set of naming conventions thatprovide additional information about what role or purpose a function has. It willbe helpful to keep some of these conventions in mind as new functions areintroduced in the coming chapters. The naming conventions depend on the typeof function involved:

A predicate is a function that returns true or false based on a test that itapplies to its input. Predicates can usually be identified by the suffix ?appended to their names. For example, the predicate list? returns true ifits argument is a list otherwise it returns false. The function odd? is anumerical predicate that returns true if its input is an odd number.A getter is a function that returns a value from a Lisp object. For example,the getter first returns the first element in a list. Getters may have thesuffix -ref appended to their names (“ref” is shorthand for "reference").For example, the function list-ref returns an element from a specifiedlocation in a list.

A setter is a function that sets a value in a Lisp object. For example, the

Page 49: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

setter list-set! puts a value at a specified location in a list. Settersalmost always have the suffix ! appended to their name. The ! informs theuser that the function literally alters the object that it sets.A constructor is a function that creates a Lisp object. For example, theconstructor list creates a Lisp list out of the values specified to thefunction. Constructors sometimes have the prefix make- added to theirname. For example, the constructor make-list creates a list of aspecified length.

A converter is a function that converts one type of Lisp object into anothertype. For example, the converter symbol->string returns a Lisp stringcontaining the name of a specified symbol. Converter names usuallycontain the “arrow” -> between their input and output types.

Function Call Syntax

Every Lisp function has a particular convention for accepting input values. Mostfunctions expect a fixed number of input values. Some functions support what

are called optional arguments — arguments that may be specified or not in afunction call. A few common functions like *, + and list allow any number ofarguments to be specified in the function call, including none. The function callsyntax for each function documented in this book is described using aspecialized format. Each argument to the function is shown as an italicizedmeta-variable, like x, that stands for a single input value in the function call. Thespecial meta-variable ... means that any number of additional input argumentsmay be specified from that point on in the function call. An input variableenclosed in square brackets, like [x], means that the enclosed argument is

optional. An optional argument is an argument that can be specified or not inthe function call. An optional argument always has a default value if theargument is not specified in the function call. Unless otherwise indicated, thisdefault value is boolean false. Consult the section Definitions in the Preface foradditional information about the format used to describe function call syntax inthis book.

The Core Functions

The following sections in this chapter present an overview of the most basicfunctions encountered in the programs discussed in this book. This core setincludes arithmetic operators, list functions and the special function define thatadds variables and functions to Lisp. Documentation for each function includesthe name of the function, its function call syntax, and a brief description of whatthe function does.

Numerical Functions

Lisp provides many numerical functions, only the most common are listed here.Consult the Scheme or Common Lisp references for the complete listing.

Page 50: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function]

[Function][Function]

[Function]

[Function]

Numerical Predicates

(number? x)

(even? n)(odd? n)

The numerical predicates return boolean true or false based on a test of their

arguments. number? returns true if x is a number, otherwise false. even? is true if

n is an even number and odd? is true if n is odd.

Interaction 6-2. Examples of numerical predicates.

cm> (number? 1)#tcm> (number? '(1))#fcm> (even? 0)#tcm> (odd? -2)#fcm>

Arithmetic Relations

(= n ...)

(< n ...)

(> n ...)

(<= n ...)(>= n ...)

The arithmetic relations return boolean true if the relation is true for allarguments, otherwise false. Inequalities of more than two arguments test theordering of the input values.

Interaction 6-3. Examples of arithmetic relations.

cm> (= 1 1.0)#tcm> (< 1 1)#fcm> (> 3 2 1)#tcm> (>= -1 0 1 1 100)#fcm> (<= -1 0 100 .3)#tcm>

Arithmetic Operators

Page 51: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function][Function]

[Function]

[Function]

[Function]

[Function][Function]

[Function]

[Function][Function]

[Function]

[Function]

(+ ...)

(* ...)

(- n ...)(/ n ...)

Return the sum, product, difference and quotient of their arguments,respectively. + and * return the operator identity (zero and one, respectively) ifno arguments are specified. - returns the negative inverse of one argument orsubtraction of two or more arguments. / returns the reciprocal of one argumentor quotient of two or more arguments.

Interaction 6-4. Example arithmetic operators.

cm> (+ 1 2 3 4)10cm> (* 10 10)100cm> (- 1)-1cm> (- 1 .5)0.5cm> (/ 1)1cm> (/ 1 .25)4.0cm> (/ 30 5 -2)-3cm> (+)0cm> (*)1cm>

Other Numerical Functions

(min n1 n2 ...)

(max n1 n2 ...)

(mod n divisor)(expt n power)

(log n)

(sin n)

(cos n)(random n)

The min and max functions return the smallest and largest of their arguments,

respectively. mod returns n modulus the divisor. exptreturns n raised to the

exponent power and log returns the exponent of n base e. sin and cos are thetrigonometric functions sine and cosine, respectively. Both sin and cos take a

single argument n in radians. The random function returns a uniform random

number between zero less than n. If n is an integer then random returns aninteger, if n is a real then a real value is returned. This means the random

Page 52: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function][Function]

[Function]

[Function]

[Function]

function can generate values in both discrete and continuous distributions (seeChapter 18 for a discussion of random distributions.)

List Functions

Lists are an essential feature of Lisp and are used everywhere in this book. Thecore arithmetic and list functions will form the foundation of your programmingvocabulary. Take the time necessary to work with these functions in theinterpreter until you are familiar with their behavior.

List Predicates

(list? x)

(null? x)

The list? predicate returns true if x is a list and null? returns true if x is theempty list.

Interaction 6-5. The list predicates.

cm> (list? '())#tcm> (list? 1)#fcm> (list? '(1))#tcm> (null? '())#tcm> (null? '(1))#fcm>

List Constructors

(list ...)

(list* x list)

(make-list len)

list returns a list that contains the arguments as elements. list* returns a list

with x added to the front of list. make-list creates a list of length len with listelements initialized to false.

Interaction 6-6. List constructors.

cm> (list)()cm> (list 1 2 3)(1 2 3)cm> (list* 1 '(2 3))

Page 53: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function][Function]

[Function]

[Function]

[Function]

[Function]

[Function]

(1 2 3)cm>

List Getters

(first list)

(second list)

(third list)(fourth list)

(rest list)

(list-ref list index)

The functions first through fourth return the first, second, third and fourth

elements in a list. The function rest returns all but the first element in list. Thefunction list-ref returns the element at the index position in list. The firstelement in a list is at index zero.

Interaction 6-7. List accessors.

cm> (define my-notes '(a b c d e))

cm> (first my-notes)acm> (rest my-notes)(b c d e)cm> (third my-notes)ccm> (list-ref my-notes 0)acm> (list-ref my-notes (random 5))dcm>

List Setters

(list-set! list index x)

Sets the element at position index in list to x.

Interaction 6-8. List setting

cm> (list-set! my-notes 0 'f)

cm> my-notes(f b c d e)cm>

Take care using setters as they “destructively” modify, or alter, what they set.We will not have occasion to use setters very often in this book.

Page 54: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function]

[Function]

[Function]

[Function]

[Function]

[Function]

Other List Functions

(length list)

(reverse list)(append ...)

(member x list)

(position x list)

length returns the number of elements in list. reverse returns a copy of list withits elements in reverse order. The append function concatenates all of itsarguments into a single list. Each argument to the function must therefore be a

list. member returns true if x is an element of list, otherwise false. position

returns the position of x in list or false if x is not in the list.

Interaction 6-9. List setting

cm> (define my-notes '(a b c d e))

cm> (length my-notes)5cm> (reverse my-notes)(e d c b a)cm> (append my-notes (reverse my-notes) my-notes)(a b c d e e d c b a a b c d e)cm> (member 'g my-notes)#fcm> (member 'e my-notes)#tcm> (position 'b my-notes)1cm> (position 'g my-notes)#fcm>

Boolean Operators

The boolean operators return either true or false based on a test applied totheir arguments. Remember that Lisp considers any value to be true unless thevalue is strictly boolean false. Scheme and Common Lisp use different valuesto designates boolean true and boolean false.

(and ...)

(or ...)(not b)

(if test then [else])

The function and returns true if all of its arguments are true, and or returns true if

any of its arguments are true. not returns the logical negation of b. The specialfunction if first evaluates the test expression. If the expression is true then the

then expression is evaluated, otherwise the optional else expression isevaluated.

Page 55: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function]

Interaction 6-10. Example of boolean operators.

cm> (and #t #t #t)#tcm> (and #f #t)#fcm> (and (= 1 1) (< 10 11))#tcm> (or #f #f #t #f)#tcm> (or #f #f)#fcm> (not #f)#tcm> (not (not #f))#fcm> (if #t 1 2)1cm> (if #f 1 2)2cm> (if (> pi 3) 'yes)#fcm> (if (< 1 3) 'winner 'loser)winnercm>

Object Functions

Lisp objects are a way to represent user defined data and are the foundationfor representing musical structure in Common Music. Objects and the functionsthat operate on them are introduced in Chapter 11.

Input and Output

(print x)

(pprint x)

(describe x)

(load file)

The print function prints x to the Lisp Listener window and returns x as itsvalue while pprint “pretty prints” x by insuring that list structures are displayedwith proper indenting so that they are more easily readable. The describe

function prints information about x. The load function loads file into Lisp andevaluates all of the expressions contained in the file.

Sequencing

A number of special functions in Lisp sequence a series of expressions, whichmeans that each expression in the sequence is evaluated one after the other.Several of these functions are discussed later in this chapter, we introduce onlythe most basic sequencing operator here.

Page 56: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

(begin ...)

begin evaluates every argument in sequential order and returns the value of thelast expression. Use begin to form a single expression out of a series ofexpressions.

Interaction 6-11. Sequential evaluation.

cm> (begin 1 2 3 'go)gocm> (if (< (random 3) 2) (begin (print 'winner) 'payday) (begin (print 'loser) 'go-to-jail))losergo-to-jailcm>

In Interaction 6-11 two begin expressions are used to define then and elseclauses, each of which holds two sub-expressions. The first sub-expression ineither clause is a print statement that displays either winner or loser to the

window depending on which clause is triggered by the test clause.

The Special Function define

Define is a special function that defines either variables or functions in Lisp.

Defining Variables

(define symbol value)

Defines symbol as a variable bound to value.

Interaction 6-12. Defining variables.

cm> (define two-pi (* pi 2))

cm> two-pi6.283185307179586cm> (define lotsa-pi (* pi pi pi))

cm> lotsa-pi31.006276680299816cm> (define favorite-pie 'apple-crunch)

cm> favorite-pieapple-crunchcm>

Page 57: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

If one studies these last interactions carefully it is possible to discover thatdefine “breaks the rules” of Lisp evaluation. In the last chapter we learned thateach argument in a function call is evaluated before the function is called.Indeed, the interactions above clearly show that the expressions (* 2 pi), (* pi pipi) and 'apple-crunch were evaluated. But notice that the first arguments to

define — the variable names — were not evaluated. If these symbols had beenevaluated, all three expressions would have produced “unbound variable” errorsaccording to Evaluation Rule 2. No error occurred because define is a specialfunction that does not evaluate its first argument. Instead, the first argument

simply names the variable or function being defined.Variables can be defined for a number of different reasons. One reason is

simply to give a name to a value. A name provides a level of abstraction upfrom the raw data and helps indicates something about the role that the dataplays in the program. For example, the variable pi used in place of the value3.141592653589793 gives a much better indication of what role that quantityserves in a program. Of course, Lisp does not actually care what name ischosen for a variable — pi could just as well be called pie or hamburger. Buttaking time to select appropriate names is an important programming taskbecause it helps to make code “self-documenting” and more intelligible to thereader.

Variables are also useful for saving the results of a calculation so that thesame value does have to be recalculated. While in principle there is nothingwrong with calculating the same value over and over again, it is important tounderstand that every instruction a computer executes takes some amount oftime to perform and a program that calculates the same value more than oncetakes longer than necessary and without any additional benefit. If thecalculations are time intensive (or if the computer is slow) the time wastedrecalculating values can become a significant issue for someone using theprogram.

Variables also stand for quantities whose values change over time orusage. Consider a program that generates twelve-tone music. If the programdefines a global variable to hold the twelve tone row then new pieces can be

generated by the exact same program simply by redefining the variable to holda new row form. The define function can be used to define new variables or toredefine an existing variable to hold a new value. When a variable is redefinedits old value is simply “thrown away” and replaced by the new value.

Another way to specify a new value for an existing variable is to use thespecial assignment function set!:

(set! symbol value)

The set! operator sets the variable named symbol to value. The differencebetween define and set! is that set! only assigns a variable a value, it doesnot actually declare the symbol to be a variable.

Interaction 6-13. Redefining and assigning variables.

cm> (define favorite-row '(0 7 8 3 4 11 10 5 6 1 2 9))

cm> favorite-row(0 7 8 3 4 11 10 5 6 1 2 9)

Page 58: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

cm> (define favorite-row (reverse favorite-row))

cm> favorite-row(9 2 1 6 5 10 11 4 3 8 7 0)cm> (set! favorite-row albans-row)

cm> favorite-row(0 3 7 11 2 5 9 1 4 6 8 10)cm>

Defining Functions

(define (name [arg ...]) body ...)

The definition of a function involves the specification of three pieces ofinformation:

1. The name of the function.2. The input parameters to the function — the“windows” through which the

function receives input arguments.3. The body of the function, the Lisp expressions that define the function's

computational behavior.

Example 6-2 provides a simple first example of a function definition and allowsus to examine these points in greater detail.

Example 6-2. Convert beats per minute to time in seconds.

(define (bpm->seconds bpm) (/ 60.0 bpm))

The code in Example 6-2 corresponds to the three parts of a function asfollows:

1. Function name: bpm->seconds2. Function parameter: bpm3. Function body: (/ 60.0 bpm)

Function Name

A function's name should reflect the programming task that the functionimplements. In our example, the name bpm->seconds was chosen because ittells the caller what the function does, namely, convert a metronome valuemeasured in beats per minute (bpm) into time measured in seconds. Ofcourse, other names are possible. Here are just a few alternatives that couldhave been used:

metronome-to-seconds

beats-per-minute->seconds

Page 59: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

metronome->seconds

metro->secs

bpm2secs

Input Parameters

A function's input parameters are the “windows” through which the functionreceives input values. Each parameter can be thought of as a variable that isautomatically bound to an argument value whenever the function is called.Statements inside the body of the function can then treat input values as

defined variables within the lexical scope or textual extent, of the function'sbody. Once the function finishes executing the input parameters are

automatically unbound such that their symbols no longer reference the valuesthat were input into the function. For example, the function definition for bpm->seconds declares a single input parameter bpm. When the function is called,this symbol will become a variable bound to the value specified as theargument in the function call. In the following function call:

(bpm->seconds 60)

the bpm parameter will be bound to the value of 60 when the function executes.When the function returns its value the symbol bpm is unbound so that itsassociation with the input value 60 no longer exists.

Function Body

The body of a function definition consists of one or more Lisp expressions thatwill be evaluated in succession each time the function is called. The value of thelast expression in the body determines the value of the function call. The body ofthe bpm->seconds definition consists of a single expression:

(/ 60.0 bpm)

The effect of this expression is to turn an input value like 60 into time measuredin seconds, as shown in Interaction 6-14.

Interaction 6-14. Converting beats per minute to time inseconds.

cm> (bpm->seconds 60)1.0cm> (bpm->seconds 120)0.5cm> (bpm->seconds 90)0.6666666666666666cm> (bpm->seconds 40)1.5cm>

Page 60: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Special Form]

[Special Form]

Local Variables

Sometimes a function defines local variables within its body to store valuesthat the function will use in its calculation. Variables can be bound anywhere ina program using the let and let* special functions:

(let (variable ...) expression ...)(let* (variable ...) expression ...)

Both forms of let define variables within the lexical scope of their bodies. The

first form in a let definition is a variable binding list. Each binding within thislist is itself a list of the form:

(variable value)

where variable is the name of the variable and value is its value. After thevariable binding list comes one or more lisp expressions that constitute thebody of the let. These expressions will be evaluated in sequential order and thelast expression will become the value of the let. Example 6-3 shows severalsample let statements:

Example 6-3. Three example let statements.

(let () 3)

(let ((a 10) (b 20)) (* a b))

(let ((a (random 10))) (* a a))

The first let does not declare any variables and simply returns the value 3. Thesecond let declares two variables a and b and binds them to the values 10

and 20, respectively. The third let binds a single variable a to a randomnumber less than 10 and then returns the square of the value. Notice that in allcases the binding list must be specified. If no variables are bound then thebindings list is an empty list.

The body of a let behaves exactly like the body of a function definition: eachexpression inside the body is evaluated in sequential order and the lastexpression determines the value of the entire let expression. Interaction 6-15 isan example of a let expression that establishes two variables, a and b, andexecutes three expressions, one after the other:

Interaction 6-15. A let definition with three expressions.

cm> (let ((a 10) (b (random 10))) (print a)

Page 61: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(print b) (list a b (+ a b)))102(10 2 12cm>

The first statement in the body of the let prints the value of a, the second printsthe random value of b and the third expression returns a list containing the valueof the two variables along with their sum. All three expressions are evaluated insequential order and the value of the last expression is returned as the value ofthe entire let statement. Note that it appears as if the let statement actuallyreturned three values: 10 2 and (10 2 12). But only the last value was actuallyreturned by the let; the first two values were printed to the window by the printstatements. The last value is printed in the window by the Listener's read-eval-print loop when the list is returned as the value of the entire let expression.

The only difference between let and let* is that let binds variables in

parallel while let* binds variables in sequential order such that the binding ofone variable can depend on the value of a preceding variable in the variable

binding list. The following let* defines two variables key and oct and returns alist containing a randomly chosen MIDI key number and the key number oneoctave above it. let* is required because the second variable, oct, depends on

the first variable, key, for its value:

Interaction 6-16. A let* definition with a dependent variable

definition.

cm> (let* ((key (random 116)) (oct (+ key 12))) (list key oct))(33 45)cm>

In this example the key variable holds a randomly chosen MIDI key Number lessthan 116. (The upper bound for the random key choice is 116 because we donot want the upper octave value to ever exceed the maximum MIDI key numberof 128). The oct variable is defined to be 12 semitones above the randomly

chosen key. Inside the body of the let* the list function creates a listcontaining the random key number and the octave above it. Since the listexpression is the last form inside the let (it is also the first form inside the let)the value returned by list becomes the value of the entire let expression.

Note that the expression in Interaction 6-16 will need be evaluated a numberof different times in order to see the effect of the random octave selection.Rather than input the same let expression over and over again we will use thisopportunity to define a new function that encapsulates the let such that it can becalled by name. Our new function (defined in Example 6-4) will be calledrandom-octave and does not require any input arguments.

Example 6-4. Encapsulation by a function definition.

Page 62: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

(define (random-octave) (let ((key (random 116))) (list key (+ key 12))))

Interaction 6-17. Calculating random octaves with afunction call.

cm> (random-octave)(21 33)cm> (random-octave)(88 100)cm> (random-octave)(17 29)cm>

Now that the calculation has been defined as a function the let expression hasbecome (potentially) useful compositional tool that can be called in differentmusical contexts or reused by other programs.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file core.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 63: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

7Etudes, Op. 2: Musical Math

Through and through the world is infestedwith quantity. To talk sense is to talk

quantities. It is no use saying the nation is

large — how large? It is no use saying that

radium is scarce — how scarce? You

cannot evade quantity. You may fly to

poetry and music and quantity and

number will face you in your rhythms and

your octaves.

— Alfred North Whitehead

The Etudes in this chapter demonstrate how the core Lisp functions introducedin the previous chapter can be used to implement new functionality in Lisp.There are often many possible ways to implement a given function; theimplementations discussed in this chapter were chosen more for theirpedagogical clarity than anything else.

Op. 2, No. 1: Rhythmic Time

In our first exercise we implement a function that converts metric rhythm valuesinto time in seconds. We will use Lisp ratios to represent metric proportions.For example, a quarter-note will be notated as 1/4, a whole note as 1, threesixteenths is 3/16, and seven twenty-third notes would be 7/23. Since the basicmetric proportions are relative to a whole note (at least for simple meters) toconvert a metric value to seconds simply requires that the ratio be multiplied by4.0, the length of a whole note in seconds. For example, 1/4*4.0 gives 1.0seconds per quarter-note, and 3/16*4.0 give .75 seconds. Of course, everymusician knows that to convert a metric proportion into seconds really requires

that a tempo factor be specified in addition to a metric value. We will use thebpm->seconds function we defined in the last chapter for just this purpose.Example 7-1 includes the definition of bpm->seconds as well as our new functionrhythm->seconds.

Example 7-1. Converting metric proportion into time in

seconds.

Page 64: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define (bpm->seconds bpm) (/ 60.0 bpm))

(define (rhythm->seconds rhy tempo) (* rhy 4.0 (bpm->seconds tempo)))

When the tempo is 60 then bpm->seconds returns 1.0 and the value returned byrhythm->seconds will be unchanged. However, a tempo of 120 produces .5 asa tempo coefficient and scales the metric values returned by rhythm->secondsin half (Interaction 7-1).

Interaction 7-1. Using the rhythm->seconds function.

cm> (rhythm->seconds 1/4 60)1.0cm> (rhythm->seconds 1/2 120)1.0cm> (rhythm->seconds 1/8 90)0.3333333333333333cm>

As an additional exercise, try defining a variation of this function that allows abeat value other than the whole note to serve as the basis for the calculation.

Op. 2, No. 1: Transposing HertzFrequency

In this next exercise we implement a function that transposes Hertz values bysemitones, quarter tones, 13th tones, or any other equal division of the octave.In order to implement this function we must first understand something about the

nature of frequency calculations. Every musician knows that an octaverepresents a doubling or halving of frequency. This means that a sequence of

frequencies spaced an octave apart can be calculated from a frequency f bysimple multiplication: 1f, 2f, 4f, 8f, 16f, and so on. A process of halving thefrequency will produce octaves downward: 1f, 1/2f, 1/4f, 1/8f, 1/16f, Doubling

and halving are exponential progressions, that is, each can be expressed as a

power of 2: 20=1, 21=2, 22=4, 23=8, 24=16, and 20=1, 2-1=1/2, 2-2=1/4, 2-3=1/8,

2-4=1/16. The the Lisp function expt can be used to calculate power of twosequences in either ascending or descending order, as shown in Interaction 7-2.

Interaction 7-2. Calculating octaves as powers of 2.

cm> (expt 2 -2).25cm> (expt 2 -1).5cm> (expt 2 0)

Page 65: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

1cm> (expt 2 1)2cm> (expt 2 2)4cm>

We can see in the previous example that as the exponents progress linearly

from -2 to 2 the values returned by expt progress exponentially. Since 20 is the

unison (1) and 21 is the octave (2) then exponents that lie between 0 and 1 willdetermine frequencies within a single octave. The exact middle of an octave

(tritone) must be 21/2, 1 semitone up from f is f * 21/12, 3 quarter-tones down

from f is f * 2-3/24, and so on. Interaction 7-3 demonstrates the use of fractionalexponents to calculate frequencies within one octave.

Interaction 7-3. Exponentiation.

cm> (expt 2 0)1cm> (expt 2 1)2cm> (expt 2 1/2)1.4142135623730951cm> (expt 2 1/12)1.0594630943592953cm> (expt 2 -3/24)0.9170040432046712cm>

The expt function is one of our core functions, and we can use it to define a newfunction that scales any hertz value by any equal tempered amount, as shown inInteraction 7-4.

Example 7-2. Scaling Hertz frequency values by equaltempered amounts.

(define scale-hz hz mul) (* hz (expt 2 mul)))

The scale-hz function multiplies an input Hertz value by an octave scaler totranspose the value by an equal division of the octave, as shown in Interaction7-4.

Interaction 7-4. Calculating Hertz values with scale-hz.

cm> (scale-hz 440 1)880cm> (scale-hz 440 -2)110

Page 66: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (scale-hz 440 1/24)452.8929841231365cm>

Op.2, No. 2: Converting MIDI KeyNumbers to Hertz Frequency

In Example 7-3 we implement a function keynum->hertz to convert MIDI keynumbers into equivalent Hertz frequencies in the equal tempered scale. Recall

that MIDI key numbers represent positions in a 128 key virtual keyboard, wherekey number 0 is the lowest C (8.175 Hertz), 60 is Middle C (261.625), and 127is the highest key (12543.853 Hz).

Example 7-3. Converting MIDI key numbers to Hertz

frequency.

(define lowest-freq 8.175)

(define (keynum->hertz knum) (* lowest-freq (expt 2 (/ knum 12)

Since an octave contains 12 semitones, the octave exponent to expt iscalculated by dividing the input key number by the number of keys in oneoctave.

Interaction 7-5. Converting MIDI key numbers to Hertz

equivalents.

cm> (keynum->hertz 60)261.6255653005986cm> (keynum->hertz 69)439.99999999999994cm>

In the last expression of the preceding interaction the key number 69 returns439.999, a value very close to, but not exactly, 440.0, the true frequency oftuning A. This small error is due to the fact that floating point numbers areinexact quantities and we are basing our calculations on a very low C. Toimplement a version of keynum->hertz in which Middle C is key number 60 butkey number 69 returns exactly 440.0 requires that the Hertz calculation bebased on a low A rather than a C, and that the key number difference betweenA and C (3 semitones) then be added to the input key number, as shown inExample 7-4.

Example 7-4. A version of keynum->hertz based on A.

Page 67: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define lowest-freq 6.875)

(define (keynum->hertz knum) (* lowest-freq (expt 2 (/ (+ knum 3) 12))))

Interaction 7-6. Converting MIDI key numbers to Hertz

equivalents.

cm> (keynum->hertz 60)261.6255653005986cm> (keynum->hertz 69)440.0cm>

Op.2, No. 3: Bounded Random Selection

Recall from the last chapter that the random function returns a value equal to orgreater than zero but less than the upper bounds specified as an argument tothe function. In this next exercise we will implement a function that generatesrandom selection between a lower and upper boundary specified by the user. Ifboth boundaries are integers then pick-range will return a discrete (integer)value, otherwise a continuous value (floating point number) that lies between thelow and high bounds will be returned.

Example 7-5. Random selection between a low and high

bounds.

(define (pick-range low high) (let ((rng (- high low))) (if (> rng 0) (+ low (random rng)) 0)))

The pick-range function converts a lower and upper bounds for random

selection to a range rng by subtracting low from high. If the difference is greaterthan zero then pick-range selects a random value less than rng and adds it

back to low so that the value returned again lies between low and high,otherwise zero is returned.

Interaction 7-7. Examples of pick-range.

cm> (pick-range 60 100)96cm> (pick-range -1.0 1.0)-0.697347603174574cm> (pick-range 440 880)789cm>

Page 68: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Op.2, No. 4: Random Selection from aList

Our next function randomly selects an element from a list of values that the userspecifies to the function.

Example 7-6. Random selection from a list of values.

(define (pick-list lst) (let* ((len (length lst)) (pos (random len))) (list-ref lst pos)))

The function pick-list first calculates a random position pos in the input listand then returns the element at that position using the list-ref accessor. Thelet* special function is used so that the value of len can be used to calculate

the value of pos. Recall that list positions range from zero to one minus thelength of the list. By specifying the length of the list as the exclusive upperbounds to random a valid position in the list is guaranteed to be chosen. Theelement returned by list-ref becomes the value of the function call (Interaction7-8).

Interaction 7-8. Examples of pick-list.

cm> (define aeolian-mode '(57 59 60 62 64 65 67 69 71 72 74 3 77 79 81))

cm> (pick-list aeolian-mode)62cm> (pick-list aeolian-mode)79cm> (pick-list aeolian-mode)71cm> (pick-list '(1/16 1/8 1/8 1/4))1/4cm>

Note that in the last interaction shown in Interaction 7-8 the rhythms list containstwice as many 1/8th notes as 1/16ths or 1/4 notes. This is one simple way to

implement weighted random selection, in which some outcomes have moreprobability of selection than others. The random function generates numbers in auniform distribution which means that there is an equal chance of any numberin the range being selected. By specifying multiple copies of an element therewill be more chances that one of its instances will be chosen.

Op.2, No. 5: Chance and Probability

Page 69: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

The function defined in Example 7-7 is a predicate function that determines if a

randomly chosen number is within the probability factor prob specified by theuser. If it is then the function returns true, otherwise it returns false (Example 7-7).

Example 7-7. Random selection from a list of values.

(define (chance? prob) (< (random 1.0) prob))

The probability factor prob should be a real number between 0.0 and 1.0

inclusive. If prob is 0.0 then there is a zero chance that the predicate will return

true because the random function never returns a value less than zero. If prob is1.0 then there is a 100% probability of chance? returning true because (random1.0) is guaranteed to return a value less than 1.0. Interaction 7-9 demonstratesseveral calls to the chance? function.

Interaction 7-9. Examples of chance?

cm> (chance? 0)#fcm> (chance? 1)#tcm> (chance? .75)#tcm> (if (chance? .8) 'retrograde 'prime)retrogradecm> (if (chance? 0) 'retrograde 'prime)primecm>

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file math.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 70: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

8Iteration and the Loop Macro

do — a deer, a female deerre — a drop of golden sun

mi — a name, I call myself

fa — a long long way to run!

so — a needle pulling thread

la — a note to follow so

ti — a drink with jam and bread

That will bring us back to ...

— Oscar Hammerstein, The Sound ofMusic

Programming tasks often consist of actions that must be repeated over and

over again. The process of repeating actions is called iteration, or looping.Lisp provides a powerful iteration facility called Loop that allows many differenttypes of iterative tasks to be described. Loop is really more of a language fordescribing iterations than it is a Lisp function. In order to introduce theexpressive power of Loop, we start by examining two very small musicprograms, both of which require iterative solutions.

Two Iteration Examples

Consider a program that transposes twelve tone rows onto specific notes in thestandard scale. Assume for the moment that notes are represented by MIDI keynumbers 0-127 and that a twelve tone row is simply a list of pitch class integers0-11. Data for this program could be defined using two variables, as shown inExample 8-1.

Example 8-1. Defining a transposition offset and a twelve

tone row.

(define my-key 60)

(define my-row '(0 3 7 11 2 5 9 1 4 6 8 10))

The iterative task that our transposition program must implement can be

Page 71: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

expressed in a single sentence:

“For each pitch class pc in the list my-row, add pc to my-key andcollect it into a new row.”

This sentence is easily translated from English into Loop:

(loop for pc in my-row collect (+ pc my-key))

Interaction 8-1. Loop implementation of the transposition

program.

cm> (loop for pc in my-row collect (+ pc my-key))(60 63 67 71 62 65 69 61 64 66 68 70)cm>

Example 8-1 implements our row transposition. Within the Loop expression, the

stepping variable named pc was automatically mapped over the list my-rowand set to each pitch class in the list. The function call (+ pc my-key) definesthe operation applied to each pitch class to effect the transposition. Thecollect command tells Loop to gather each transposed value into a new listand to return that list as the value of the entire loop expression.

Our second program computes a list of Hertz frequencies from the harmonicseries. The fundamental, starting harmonic number and ending harmonicnumber are defined in Example 8-2.

Example 8-2. Defining the fundamental and bounds for aharmonic series.

(define fundamental 220)

(define start-harm 1)

(define end-harm 8)

The iterative task that our program must implement can be expressed asfollows:

“For each harmonic number h from start-harm to end-harm, multiply

fundamental by h and collect it into a list.”

This is implemented by the Loop expression:

(loop for h from start-harm to end-harm collect (* fundamental h))

Interaction 8-2. Loop implementation of the harmonic

series iteration.

cm> (loop for h from start-harm to end-harm

Page 72: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

collect (* fundamental h))(220 440 660 880 1100 1320 1540 1760)cm>

Both Interaction 8-1 and Interaction 8-2 repeat a single task, or action, over arange of values. The first loop defined the iteration over elements in a list andits task was to add a transposition offset to a pitch class. The second loopdefined the iteration over a range of integers and its task was to multiply thefundamental frequency by each harmonic number. The Loop language allowsboth of these examples to be expressed in a simple and clear manner. Notethat in neither case did we have to explicitly increment stepping variables, ortest when the iteration should stop, or even construct the list that was returnedby the loop! The loop language took care of these chores automatically fromour description of the iteration.

The Loop Facility

Loop is a macro that defines an English-like syntax for describing iterativetasks. The special syntax of Loop makes it very easy for a programmer toimplement different types of iterations, from simple “repeat” loops to multipletasks performed in parallel. Note that — like the special function define — theloop syntax is so natural that we may not notice the fact that its syntax actually“breaks the rules” of normal Lisp evaluation! This can be shown by analyzingwhat should happen when the following loop expression is evaluated:

Interaction 8-3. A loop that sums numbers from zero toten.

cm> (loop for i to 10 sum i)55cm>

If the loop expression in Interaction 8-3 were treated as regular function call thenEvaluation Rule 2 should signal an “unbound variable error” because theunquoted symbol for is not a defined variable. An error does not occurbecause loop is a macro function (Chapter 2) that defines its own evaluation

strategy. Loop treats symbols like for, to and sum as command names ratherthan variables. Many introductory Lisp books and courses eschew Loopprecisely because it does not follow the standard evaluation model.Unfortunately this means the student must learn other, more cumbersomemethods to achieve the same effect more clearly expressed using Loop. Thisbook expresses all iteration and mapping tasks using the Loop language forseveral important reasons:

Loop's English-like syntax makes is very natural and easy to learn.Loop can express many different types of iteration.Loop syntax is used by the process macro introduced in Part 2 of thisbook.

Page 73: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Clause]

The process iteration macro supports algorithmic generation of music. So ifyou can write a loop then you are ready to start composing algorithmiccompositions!

Loop Syntax

A Loop expression consists of one or more clauses. A number of differenttypes of clauses may appear within the scope of a single Loop expression:

Initialization clauses establish starting conditions for the iteration.

Stepping clauses limit the iterative process and provide steppingvariables whose values automatically change each time through the loop.

Action clauses define task actions and allow values to be returned fromthe loop expression.

Conditional clauses allow action clauses to depend on the truth or falsityof a test.

Finalization clauses execute immediately after the iteration stops.

Stepping Clauses

Stepping clauses place limits on the number of times a loop executes and may

also increment stepping variables each time through the loop. More than onestepping clause may be defined in a single Loop expression. In the case ofmultiple stepping clauses, the entire loop stops as soon as the first steppingclause terminates.

repeat expr

The repeat clause repeats the loop expr number of times.

Interaction 8-4. The repeat clause.

cm> (loop repeat 10 collect (random 100))(9 3 93 58 23 0 79 48 27 60)cm> (loop repeat (random 5) sum 10)30cm>

for var = expr [then expr2]

Sets variable var to the value of expr on each iteration. If an optional then

clause is supplied then expr becomes the initial value of var and expr2becomes its value on subsequent iterations.

Interaction 8-5. The for clause.

cm> (loop repeat 5 for x = (random 100) minimize x)12

Page 74: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Clause]

[Clause]

[Clause]

cm> (loop repeat 5 for x = -99 then (random 10) collect x)(-99 4 8 6 4)cm>

for var [from low] to end [by step]

Increments var until equal to end and then stops iteration. If from is not

specified then low always defaults to 0. If by is not specified then step alwaysdefaults to 1.

Interaction 8-6. The for clause.

cm> (loop for i to 10 collect i)(0 1 2 3 4 5 6 7 8 9 10)cm> (loop for i from -1 to 2 by .5 collect i)(-1 -0.5 0.0 0.5 1.0 1.5 2.0)cm>

for var [from low] below end [by step]

Increments var while less than end.

Interaction 8-7. The below clause.

cm> (loop for i below 10 by 2 collect i)(0 2 4 6 8)cm>

for var from top downto end [by step]

Decrements var until equal to end.

Interaction 8-8. The downto clause.

cm> (loop for i from 0 downto -10 by 2 collect i)(0 -2 -4 -6 -8 -10)cm>

for var from top above end [by step]

Decrements var while greater than end.

Interaction 8-9. The above clause.

cm> (loop for i from 10 above 2 collect i)(10 9 8 7 6 5 4 3 2 )cm>

Page 75: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

[Clause][Clause]

for var in list [by fn]for var on list [by fn]

The in clause maps var over successive elements in list. An optional by

function increments the list. The on clause maps successive tails of list.

Interaction 8-10. Example list iterations.

cm> (define test '(a b c))

cm> (loop for v in (reverse test) collect v)(c b a)cm> (loop for v in test collect (list v v))((a a) (b b) (c c))cm> (loop for v on test collect v)((a b c) (b c) (c))cm>

Action Clauses

Action clauses implement iteration tasks and return values from the loop.

do expr ...

Evaluates every expr on each iteration. Returns boolean false.

collect expr

append expr

The collect clause collects values of expr each iteration and returns the values

in a list while the append clause appends the contents of expr, which mustevaluate to a list.

sum expr

minimize expr

maximize expr

count expr

thereis expr

Return the sum, minimal and maximal values of expr from the loop, respectively.

The count clause returns the number of times expr is true and thereis stops

iteration and returns true from the loop as soon as expr is true, otherwise itreturns false.

Interaction 8-11. Example action clauses.

cm> (loop repeat 2 do (print 'hi))hi

Page 76: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Clause]

[Clause][Clause]

[Clause]

hi#fcm> (loop repeat 10 collect (random 50))(43 42 27 18 26 20 44 43 23 30)cm> (loop repeat 10 sum (random 50))176cm> (loop for i below 10 count (even? i))5cm> (loop repeat 10 minimize (random 50))2cm> (loop repeat 10 maximize (random 50))43cm> (loop for x below 10 thereis (= x -1))#fcm>

Conditional Clauses

Conditional clauses place conditions on the evaluation of action clauses, or onhow many times the loop will iterate.

when test action [and ...]

unless test action [and ...]

while test ...

until test ...

The when clause executes the action clause only if test is true. The optional andoperator makes multiple actions dependent on a single test. The unless clause

executes the action clause only if test is false. The while clause terminates

iteration if test is false, and the until clause terminates if test is true.

Interaction 8-12. Example conditional clauses.

cm> (loop for i below 10 when (even? i) collect i)(0 2 4 6 8)cm> (loop for i below 10 unless (even? i) collect i)(1 3 5 7 9)cm> (loop for n = (random 10) until (even? n) collect n)(9 3)cm> (loop for n = (random 10) while (even? n) collect n)()cm> (loop for i below 10 for j = (random 10) when (even? j) collect i and collect j)(2 2 4 6 7 4 8 0 9 4)cm>

Initialization Clauses

with var [= expr] [and ...]

Page 77: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Function]

Defines var as a local loop variable. var is initialized to false unless it isexplicitly set using the optional = operator. The optional and operator allowsmore than one variable to be defined by the with clause.

Interaction 8-13. Example initialization clause.

cm> (loop with hz = 220 for h from 1 to 8 collect (* hz h))(220 440 660 880 1100 1320 1540 1760)cm>

Finalization Clauses

finally expr

(return expr)

Evaluates expr after the iteration has stopped. The return function can be usedanywhere inside a loop to return expr as its value.

Interaction 8-14. Example finalization clause.

cm> (loop repeat 10 for sum = 0 then (+ sum (random 100)) finally (return (/ sum 10.0)))48.6cm>

Advanced loop Features

The iteration possibilities afforded by Loop are almost limitless. The followingbrief discussion points out two common uses of loop that involve more than oneclause.

Parallel clauses

More than one stepping clause or action clause can be defined in a single loop.In the case of multiple stepping clauses the first clause that terminates will stopthe entire loop.

Interaction 8-15. Multiple stepping clauses.

cm> (loop for i below 4 for j from 100 by -10 for k = (random 100) collect (list i j k))((0 100 1) (1 90 92) (2 80 81) (3 70 2))

Page 78: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

cm> (loop for i below 4 collect i collect (expt 2 i))(0 1 1 2 2 4 3 8)cm>

Loops inside of loops

A single loop may contain other loops inside it.

Interaction 8-16. A loop in a loop.

cm> (loop for i from 0 by 10 to 20 collect (loop repeat 4 for j from i collect j))((0 1 2 3) (10 11 12 13) (20 21 22 23))cm>

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file loop.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 79: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

9Etudes, Op. 3: Iteration, Rows andSets

In this chapter we work through examples that use iteration to implement somebasic twelve-tone and set theory functions.

Op. 3, No. 1: Converting MIDI KeyNumbers to Pitch Classes

In the first exercise we implement list->pcs, a function that converts a list ofMIDI key numbers into pitch classes. A pitch class can be thought of as the“remainder” of a key number after all octave information has been “subtractedout”. Since there are only 12 divisions per octave in the standard scale thereare only 12 pitch classes. We will represent pitch classes using the integers 0-11. For example, all C key numbers will be pitch class 0, all F-sharps and G-flats are pitch class 6, and so on.

The Lisp function mod can be used to convert a key number into a pitchclass. The mod function returns the absolute value of the remainder from thedivision of two integers. We can use loop to see the effects of mod on a rangeof numbers:

Interaction 9-1. Using loop to collect numbers mod 12.

cm> (loop for i from 60 to 72 collect (mod i 12))(0 1 2 3 4 5 6 7 8 9 10 11 0)cm> (loop for k from 0 downto -12 collect (mod k 12))(0 1 2 3 4 5 6 7 8 9 10 11 0)cm>

Given the mod mod operator our first exercise is easy to implement. We split ourprogramming task into two small functions. The first function uses mod to converta single key number into a pitch class. The second function converts a list ofkey numbers into a list of pitch classes by mapping the first function over eachelement in the list.

Example 9-1. Converting key numbers to pitch classes.

Page 80: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define (keynum->pc k) (mod k 12))

(define (list->pcs knums) (loop for k in knums collect (keynum->pc k)))

list->pcs uses loop to iterate a variable k over every key number in a list ofkey numbers input into the function. The collect clause collects the results ofkeynum->pc into a new list and returns that list as the value of the loop. Since theloop expression is the last expression in the body of the function definition, thevalue returned by loop becomes the value of the function.

Interaction 9-2. Pitch classes from measure 15 of Berg'sViolinkonzert.

cm> (define albans-notes '(55 58 62 66 69 72 76 80 83 85 87 89))

cm> (list->pcs albans-notes)(7 10 2 6 9 0 4 8 11 1 3 5)cm>

Op. 3, No. 2: Normalizing Pitch Classes

Note that in Interaction 9-2 the series of pitch classes returned by list->pcs isclose to, but not the same as, a twelve-tone row. To form a twelve-tone row wewould need to convert the list of pitch classes into a list of intervals, where thefirst interval in the row is 0, the “root” for row transpositions. We will take thisopportunity to define a new function that normalizes a list of pitch classes or keynumbers into a list of zero-based intervals:

Example 9-2. Normalizing a list of pitch classes or keynumbers.

(define (normalize-pcs knums) (loop with root = (first knums) for k in knums collect (keynum->pc (- k root))))

In this function a loop “normalizes” the input list by subtracting the first number inthe list from all the numbers in the list. Recall that the with clause is used toinitialize, rather than step, a looping variable. The expression with root =

(first knums) directs loop to bind the variable root to the first element in theinput list. The key feature to remember about a with clause is that it happensjust one time, immediately before the iteration starts. (A for clause, on the otherhand, sets variables each time through the loop.)

Interaction 9-3. Normalizing pitch classes.

Page 81: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (define albans-notes '(55 58 62 66 69 72 76 80 83 85 87 89))

cm> (normalize-pcs (list->pcs albans-notes))(0 3 7 11 2 5 9 1 4 6 8 10)cm>

Op. 3, No. 3: Matrix Operations

In Example 9-3 we implement four functions that perform the core manipulationsfor twelve-tone composition. The function retrograde-row returns theretrograde version (reversal) of a row. The function transpose-row shifts therow to a new pitch class. The function invert-row inverts a twelve-tone row.The function row->matrix returns a list of lists that represents the twelve rows ofa “Prime by Inversion” matrix. The utility function print-matrix prints a matrixso that each row appears on its own line.

Example 9-3. Twelve-Tone Functions.

(define (retrograde-row row) (reverse row))

(define (transpose-row row to) (loop for pc in row collect (keynum->pc (+ pc to))))

(define (invert-row row) (loop for pc in row collect (keynum->pc (- 12 pc))))

(define (retrograde-invert-row row) (retrograde-row (invert-row row)))

(define (row->matrix row) (loop for i in (invert-row row) collect (transpose-row row i)))

(define (print-matrix matrix) (loop for row in matrix do (print row)))

The function retrograde-row is the easiest to implement, it simply calls thecore function reverse to reverse the order of the input list. Notice that bydefining this function we have essentially renamed reverse so that it betterreflects our application's use of it. The function transpose-row iterates over aninput row and adds an offset to each element. Notice that we filter this valuethrough our keynum->pc function so that we our result list is guaranteed toremain pitch classes even after the addition of the offset. The function invert-row is similar to transpose-row except that it subtracts rather than adds anoffset from each pitch-class in the input list. The function retrograde-invert-row can be implemented simply by calling two functions we have alreadydefined in our implementation. The last two functions operate on a matrix oftwelve-tone rows. The function row->matrix computes the prime-by-inversion

Page 82: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

matrix for given a row by transposing the row to each interval in the row'sinversion. Note that this function returns a list of twelve lists: the outer listrepresents the matrix and each sublist represents one transposition of theoriginal row. The function print-matix can be used to display the matrix so thateach row is printed on a single line. Note that #8212; unlike all the otherfunctions in our example #8212; this last function is called for its effect(displaying the matrix) rather than to calculate and return a value. Interaction 9-4demonstrates using our functions on the row from Alban Berg's Violin Concerto.

Interaction 9-4. Twelve-tone matrix operations.

cm> (define albans-row '(0 3 7 11 2 5 9 1 4 6 8 10))

cm> (retrograde-row albans-row)(10 8 6 4 1 9 5 2 11 7 3 0)cm> (transpose-row albans-row 3)(3 6 10 2 5 8 0 4 7 9 11 1)cm> (invert-row albans-row)(0 9 5 1 10 7 3 11 8 6 4 2)cm> (retrograde-invert-row albans-row)(2 4 6 8 11 3 7 10 1 5 9 0)cm> (print-matrix (row->matrix albans-row))(0 3 7 11 2 5 9 1 4 6 8 10)(9 0 4 8 11 2 6 10 1 3 5 7)(5 8 0 4 7 10 2 6 9 11 1 3)(1 4 8 0 3 6 10 2 5 7 9 11)(10 1 5 9 0 3 7 11 2 4 6 8)(7 10 2 6 9 0 4 8 11 1 3 5)(3 6 10 2 5 8 0 4 7 9 11 1)(11 2 6 10 1 4 8 0 3 5 7 9)(8 11 3 7 10 1 5 9 0 2 4 6)(6 9 1 5 8 11 3 7 10 0 2 4)(4 7 11 3 6 9 1 5 8 10 0 2)(2 5 9 1 4 7 11 3 6 8 10 0)cm>

Op. 3, No. 4: Determining Normal Order

The normal order of a group of notes is the most “tightl packed” permutation ofits interval content. The most tightly packed permutation is the version of the setwith the smallest total span. In the case of a “tie”, the permutation with smallerintervals leftward is the winner. For example there are four permutations of adominant seventh chord:

Table 9-1. Inversions of a dominant seventh chord.

inversion intervals

root (0 4 7 10)

first (0 3 6 8)

second (0 3 5 9)

third (0 2 6 9)

Page 83: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The set (0 3 6 8) is the normal order because this permutation spans only 8semitones and is therefore the most compact.

The first step in computing the normal order of a group of notes is to definea function that accepts a list of key numbers and returns its pitch class contentwith no duplications. We will implement this behavior using a “scratch pad” listthat represents the 12 possible pitch classes in an octave. Each of the twelveelements in the list will represent the pitch class whose number corresponds tothe element's position in the list. So the 0th element in the list corresponds topitch class 0, the element at position 1 corresponds to pitch class 1, and so on.Each element in the scratch pad list is initially set to false. A false elementmeans that a pitch class for that position has not yet been recorded in the list.Pitch classes are recorded in th scratch pad by iterating over the input note listand — for each pitch class encountered in the input list — setting itscorresponding element in the scratch pad to true. Since all elements are initiallyfalse, only pitch classes that are encountered one or more times will be set totrue. Once every pitch class in the input list has been recorded, a second loopwill iterate through the scratch pad list and return only those positions in thescratch pad for which a pitch class was actually recorded (Example 9-4).

Example 9-4. Determining pitch class sets.

(define (reduce-pcs knums) ;; create a scratch pad list with 12 locations to ;; remember the pitch classes in knums. each element ;; in the pad is initially false. (let ((pad (make-list 12))) ;; remember each pitch class in knums by setting ;; its position in the scratch pad to true. (loop for k in knums for pc = (keynum->pc k) do (list-set! pad pc true)) ;; iterate over the scratch pad and return ;; a list of the positions where pitch classes ;; were recorded (loop for i in pad for j from 0 when i collect j)))

The function reduce-pcs defines the local variable pad to hold a “scratch pad”list of 12 elements. make-list automatically sets each element in this list tofalse. The body of reduce-pcs consists of two loops. The first loop iterates overthe input list and, for each pitch class it encounters, sets the elementcorresponding to that pitch class in the scratch pad to true. The loop doesn'treturn anything; its do clause simply invokes the function list-set! to set eachelement at the specified position to true.

The second loop in the body consists of three clauses. The first clause fori in pad maps the variable i over every value in pad. Since the pad contains

only true or false values, the i variable is set to true at positions where a pitchclass was recorded, otherwise it remains false. The second clause for j from

0 simply steps the variable j in parallel with the position of i in the scratch pad.

The third clause when i collect j collects the value of j only when the value of

i is true. The loop therefore returns exactly those positions in the scratch pad at

Page 84: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

which a pitch class had been recorded. This second loop is the last form in thebody of the function so its value is returned as the value of the function call.

Interaction 9-5. Calling reduce-pcs on two octaves of G

dom7.

cm> (define g-dom7 '(55 59 62 65 67 71 74 77))

cm> (reduce-pcs g-dom7)(2 5 7 11)cm>

The g-dom7 variable defines a G dominant 7th chord doubled over twooctaves. reduce-pcs returns the list of pitch classes in that chord with allduplicates removed. (The pitch class 2 is D, 5 is F, 7 is G and 11 is B).

The next step is to define a function that permutes, or rotates, a list of pitchclasses so that each of its inversions can be tested for compactness. To rotatethe list to the next inversion means to move the element that is currently at thefront of the list to the back of the list. In order to do this we first "destructure", orsplit, the list into two separate lists. The first will contain all but the first element,and the second will contains only the first element. The Lisp function rest willreturn the first list we need:

Interaction 9-6. A list of all but the first element.

cm> (define test-set '(2 5 7 11))

cm> (rest test-set)(5 7 11)cm>

The second list must contain only one element: the pitch class that is currently atthe front of the input list. The Lisp accessor first returns the first element of alist, the function list can then be used to turn that value into a list:

Interaction 9-7. A list of only the first element.

cm> (first test-set)2cm> (list (first test-set))(2)cm>

Given the two destructured lists in Interaction 9-7 and Interaction 9-6 we canuse the Lisp function append to “glue” these lists back together in the order wewant:

Interaction 9-8. Reordering the destructured lists.

Page 85: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (append (rest test-set) (list (first test-set)))(5 7 11 2)cm>

We are now ready to implement our function that permutes a pitch class set:

Example 9-5. Permute pitch class set.

(define (permute-set set) (let ((front (list (first set))) (others (rest set))) (append others front)))

Interaction 9-9. Examples of set permutation.

cm> (permute-set test-set)(5 7 11 2)cm> (permute-set (permute-set test-set))(7 11 2 5)cm>

The last helper function we need to define is called set-weight, a function thatreturns a measure of how compact a set is. Rather than checking the span ofeach inversion and then checking “leftward packing” in case of a tie, weimplement a single function that returns a total measure of the compactness ofa set. To do this we will use two functions that have already been introduced:the Lisp function expt and our own function normalize-pcs. Our method is quitesimple: to measure the compactness of a pitch class list we will first convert thelist into a normalized (zero based) set and then treat each number in the set asexponents. The power of 2 value of each exponent (interval) will then besummed together to determine the total weight. By summing exponential valuesa single large interval will always weigh more than smaller ones addedtogether.

Example 9-6. Set permutation.

(define (set-weight set) ;; set must be normalized (zero based) (loop for s in set sum (expt 2 s)))

Interaction 9-10. Comparing inversion weights of a dom7.

cm> (set-weight '(0 4 7 10))1169cm> (set-weight '(0 3 6 8))329cm> (set-weight '(0 3 5 9))553cm> (set-weight '(0 2 6 9))

Page 86: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

581cm>

The set-weight function correctly identifies (0 3 6 8) as the most compactrotation, i.e. the normal order, of the dominant 7th chord. We are now ready toimplement the actual function:

Example 9-7. Computing the normal order of a note set.

(define (normal-order notes) (let ((pclist (reduce-pcs notes))) (loop with memory and winner repeat (length pclist) for normal = (normalize-pcs pclist) for weight = (set-weight normal) do (if (or (not winner) (< weight memory)) (begin (set! winner normal) (set! memory weight))) (set! pclist (permute-set pclist)) finally (return winner))))

The normal-order function first calls our helper reduce-pcs to convert the inputlist into a list of pitch classes with all duplicate key numbers removed. The mainprocessing loop then iterates as many times as there are pitch classes in

pclist. The winner variable will hold the permutation of pclist with the smallest

weight, i.e. the normal order of the note list. The variable memory will be used

to “remember” the smallest weight as each inversion of pclist is compared tothe previous one. On each iteration of the loop, the stepping variable normal is

set to the normalized version of pclist and its weight is calculated by the set-weight function. The do action clause contains two Lisp expressions: an ifconditional statement and a set! assignment statement. The purpose of theassignment statement (set! pclist (permute-set pclist)) is to reset the

pclist variable at the end of the loop so that when the loop repeats the variablealready contains the next inversion of the set to check. The purpose of the ifstatement is to establish the very first version of pclist as the “winner” unless a

subsequent inversion can be shown to have a smaller set weight. The thenclause of the if statement actually consists of two assignment statements that

are grouped into a single then expression using the begin function. (Without thesurrounding begin expression the first set! expression would be interpreted as

the then clause and the second set! expression would be considered to be an

else clause!) The test expression: (or (not winner) (< weight memory))

evaluates to true if either (1) the winner variable has not been set (i.e. it is still

false from the initial with clause) or (2) the current value of weight is less than

the current value of winner. The finally clause executes at the very end of the

loop, after every inversion of pclist has been checked. The finally clause usesthe return function to return whatever version of pclist was saved in winner.Since the loop is the last statement in the body of the function, the value that theloop returns becomes the value that the function returns.

Page 87: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Interaction 9-11. Computing normal orders.

cm> (normal-order g-dom7)(0 3 6 8)cm> (normal-order '(72 64 67))(0 4 7)cm> (normal-order '(65 64 75 70))(0 1 2 7)cm>

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file sets.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 88: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

10Parameterized Sound Description

Of all noises, I think music is the leastdisagreeable.

— Samuel Johnson

The introduction to this book identified three representation levels, or layers,that music composition applications commonly address (Figure 10-1).

Figure 10-1. The composition abstraction levels.

Applications that operate at the acoustic level record and output sound basedon a digitized representation of an acoustic waveform. Computer applications

in the score level use a symbolic encoding to represent specific attributes, orqualities, of sound. In the metalevel, compositional algorithms, processes andrelationships are expressed. Each of these levels is a layer of abstraction thatencapsulates, or hides, details in the next lower level. Figure 10-2 shows asingle musical example depicted at the three abstraction levels. Each depictionis represented as a file containing an encoding of the example and an estimateof its size in bytes. The score and acoustic representations (Table 10-2 andTable 10-3) are generated automatically from the single metalevel constructshown in Table 10-1.

Table 10-1. Metalevel representation of a self-similar

process.

sier.cm: 8 lines, 225 bytes.

(define (sierpinski knum ints dur depth) (process for i in ints

Page 89: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

;; output new sound event output (new midi :time (now) :keynum (+ knum i) :duration dur :channel depth) when (> depth 1) sprout (sierpinski (+ knum i 12) ints len (/ dur 3) amp (- depth 1)) wait dur))

Table 10-2. MIDI and Csound score level sound events

from Table 1.

sier.mid: 250 messages, 1000bytes.

sier.sco :117 lines, 4200bytes.

0 <Note-On 0 25 63> 0 <Note-On 0 37 63> 0 <Note-On 0 49 63>53 <Note-Off 0 49 127> 0 <Note-On 0 56 63>53 <Note-Off 0 56 127> 0 <Note-On 0 54 63>53 <Note-Off 0 37 127>...

i3 0.000 1.000 34.647; i2 0.000 0.333 69.295; i1 0.000 0.111 138.591;i1 0.111 0.111 207.652;i1 0.222 0.111 184.997;i2 0.333 0.333 103.826;i1 0.333 0.111 207.652;i1 0.444 0.111 311.126;...

Table 10-3. Acoustic level waveform samples from Table

2.

sier.snd: 350,000 samples, 700,000 bytes.

732e 646e 0000 1c00 0000 0000 0000 0300 0000 2256 0000 0100 0000 0000 d0ff a0ff 75ff 44ff 0cff e9fe c9fe 74fe 27fe 8dfe b1ff b100 ef00 d100 e300 0c01 1201 0c01 1c01 3f01 6801 7301 3f01 2701 c301 ff023504 1b05 b605 cf05 8305 9705 3b06 9006 5b06 8d06 1007 4106 d803 ff01 2d02 1803 df02 3d01 fcfe 15fd 73fc 03fd 6ffd fdfc 91fc c6fc e5fc 96fc 8bfc ...

The translation from the metalevel to the score level is achieved in part by

representing sound symbolically in the metalevel, using an abstraction called aparameterized sound event. A sound event is a generalized description ofsound information from which different score level encodings can be computed.The ability to produce different encodings of the same information is importantbecause there are many applications in the score level that overlap infunctionality but differ in the exact format they use to encode sonic information.Parameterized sound description allows the composer to represent sound in ageneral, consistent, and uniform manner while still being able to address thevarious applications and languages that define the score and synthesiscomposition levels.

Parameterized Sound Events

Page 90: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

A parameterized sound event encodes sound as a collection of propertiescalled sound parameters. The first step in the encoding process is to define anappropriate set of parameters that will adequately represent the characteristicsof a type of sound. Once these parameters have been determined, specific

instances of the sound can be created by determining the set of parametervalues that constitute each instance of the sound. While all sounds have a basicset of properties that event descriptions encode, the complete set ofparameters for a given sound event will depend both on the nature of the soundas well as on the score level synthesis algorithm that is used to produce itsacoustic waveform. A useful way to think about sound parameters is that theydefine dimensions of compositional interest. Coordinates for each “soundpoint” are expressed by values along each parameter's axis. The job for thecomposer, then, is to articulate interesting sound points in a multi-dimensionalparameter space. Many score level applications display sound points asgraphical plots that display parameterized sound data. For example, notationprograms map event parameters onto the traditional five-line staff of CommonPractice Notation. Many MIDI sequencers provide a display style called “pianoroll notation” that maps two (or more) sound parameters onto a two dimensionalgrid, as shown in Figure 10-2.

Figure 10-2. Piano roll graph displaying sound events from

Table 2. The graph displays four sound parameters: thehorizontal axis graphs start-time and duration, the vertical

axis graphs frequency, and color is assigned to timbre(channel).

It will be useful to remember that all the metalevel techniques that are presentedin the remainder of this book must ultimately produce patterns of dataorganized into sound event descriptions. We begin learning about this processby defining some basic properties of sound that all events encode anddiscussing some common data formats used in parameterized eventdescription.

The Properties of Sound

When a sound source vibrates, its oscillations disturb molecules in thesurrounding medium. The elastic nature of the medium causes a chain reaction

Page 91: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

to occur as the disturbance is propagated from molecule to molecule. The

result is a series of waves that radiate outward from the sound source in alldirections. These sound waves consist of compressions and rarefaction ofmolecules in the medium brought about by the back and forth motion of thesound source. When molecules are pushed together (compression), thepressure in that vicinity of the medium is higher than in regions still atequilibrium. Similarly, when vibration in the opposite direction pulls moleculesapart (rarefaction), the pressure at that locality is less than at points in themedium still at equilibrium. This continuous, smooth change in pressure oneither side of an equilibrium point has a shape that can be graphed over time(Figure 10-3).

Figure 10-3. The scatter plot depicts the density of

molecules in the medium 1at an instant in time. The line

graph depicts pressure as a function of time. Equilibrium,compression, and rarefaction are labeled E, C and R,

respectively.

Figure 10-3 plots a waveform as a change in pressure over time. The vertical

axis displays the amplitude of the waveform, a measurement of the amount ofenergy transmitted in its compressions and rarefaction. The waveform in Figure10-3 can be seen to oscillate in a very regular way. The unit of regularity in a

waveform like this is called a cycle, and the time a cycle takes to occur is called

a period. A wave that oscillates at a completely uniform rate such as the one inFigure 10-3 is called a sine wave and the rate of oscillation of a sine wave is

called frequency. Frequency is typically measured in Hertz, the number ofcycles per second the waveform produces. Sine waves are the basic building

blocks of all sound. However, most musical instruments generate complex

waveforms rather than sine waves. A complex waveform oscillates at multiplerates (frequencies) at the same time (Figure 10-4). When these component

frequencies are related by integer ratio, the sound is said to be harmonic.

Harmonic sound is generally perceived as a single fundamental frequency withan associated timbre or tone color. Most, but not all, musical instruments

produce harmonic sound. In contrast, aperiodic waveforms, or noise, have littleor no discernible regularity in their vibration.

Page 92: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Figure 10-4. Plots of three different types of waves. A sine

wave (blue, small amplitude), a complex waveform

containing odd harmonics (green, medium amplitude), andnoise (red, large amplitude).

Sound Properties and Event Parameters

The preceding discussion of sound waves identifies some fundamental

properties of sound. All sounds are comprised of one or more frequency

components. The mixture of these components imparts a characteristic timbre,or tone color to the sound. Sound transmits a certain amount of amplitude, or

energy, to our ears. All sounds also start and last for some duration of time.These five basic properties: start, duration, frequency, amplitude, and timbreare present either implicitly or explicitly in all parameterized sound eventdescriptions (Table 10-4 and Table 10-5).

Table 10-4. Sound event parameters for MIDI.

parameter property value

time time 0.0

duration time 0.5

keynum frequency 60

amplitude amplitude 64

channel timbre 0

Table 10-5. Sound event parameters for FM synthesis.

parameter property value

time time 0.0

duration time 3

frequency frequency 440

amplitude amplitude 0.2

amplitude-env amplitude (1 0 .25 .1 1 0)

mratio timbre 2

index timbre 4

Page 93: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

index-env timbre (0 0 .5 1 1 0)

degree amplitude 0

distance amplitude 1

reverb timbre .01

Of course additional sound properties could be added to the list, andproperties may not correspond in a one-to-one manner with sound eventparameters . For example, natural sounds have a location associated with thembut this property may or may not be represented in a particular sound event.The property of timbre, on the other hand, is part of every sound but it is socomplex that its specification may be spread over several parameters (Table10-5), or only implicitly represented by the choice of instrument that renders thesound event description (Table 10-4).

Parameter Data Formats

When viewed from the perspective of sound events, the activity of musiccomposition consists of projecting musically interesting patterns into dataassigned to event parameters. But we have seen that a single sound propertymay be expressed differently in two sound events. Moreover, each sound eventmay require its parameters to be described in a particular way, by usingspecific formats for data. It is important, then, that description at the metalevelbe flexible and interchangeable so that the composer can work with whateverdata formats are most appropriate for a given situation.

Frequency Formats

Over the course of centuries Western composers have developed many waysto organize and relate frequency information. Some of these methods definerelationships between absolute frequency values while others describefrequency in terms of relative distances, or motion. In computer composition,some score level applications may present an additional constraints on how acomposer formats, or encodes frequency information. For example, the MIDIprotocol insists that frequency information be encoded as integer “keynumbers”. Notation programs, on the other hand, represent frequency in a moresymbolic way that permit more that one “spelling” for a given frequency, andsound synthesis languages such as Common Lisp Music and Csound usuallyexpect frequency to be encoded as raw Hertz values. Because there are somany ways to work with frequency, we will identify four interchangeable formatsfor its expression:

in Hertz, or cycles per second

as keynum positions in a scale

as note names in a scale

as interval distances between notes or key numbers

A Hertz value specifies frequency as the number of cycles per second of its

Page 94: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function]

waveform. Many synthesis languages expect frequency in Hertz because thisformat does not require an underlying tuning system or mode for its

interpretation. In contrast to Hertz, the note and keynum formats convert to afrequency given the existence of some underlying scale that quantizesfrequency space into discrete steps. The frequency of each scale degreedepends on the tuning system that the scale employs. For example, thestandard chromatic scale uses an equal tempered tuning system based on a C

at 8.175 Hz with an interval of 21/12 (100 cents) between each scale degree.A keynum specifies the scale degree position of a frequency in a scale.

Since a keynum is a numerical quantity like Hertz it can be used in arithmeticcalculations. But unlike Hertz, keynums also encode pitch class and octaveinformation and allows intervals to be calculated using addition and subtractionrather than multiplication and division. The MIDI protocol uses integer keynumbers to identify specific tones that a Note-On message activates when it issent to a MIDI synthesizer. Common Music extends the idea of key number toinclude floating point key numbers, such as 60.5 or 45.678, so thatfrequencies “in between” adjacent scale degrees in a tuning system can be

specified. A floating point key number is interpreted kkk.cc where kkk is the

integer key portion of the keynum and the fractional portion .cc is interpreted as

cc cents above the Hertz value of kkk.

A note is a Lisp symbol that names a scale degree. Although note namescan vary from scale to scale as defined by the composer, a note namegenerally consists of a basic class name such as a or nem, an (optional)accidental sign and an octave number. For example, a4 is the note name for440 Hz in the standard chromatic scale, cs5 is C-sharp in the fifth octave, andnem0 is the first note in the first octave of the Pelog scale (Chapter 15). Notenames are the most generally useful frequency format because they encodeinformation about the spelling of a frequency in addition to its Hertz frequency,pitch class and octave register.

An interval is an integer that specifies the distance between two scaledegrees. This distance can be expressed as a positive or negative number of

steps between degrees or as a special typed interval that encodes informationabout an interval's spelling in addition to its size. See the Common Musicdictionary for more information about typed intervals.

In a given compositional context there may be one frequency representationthat has advantages over the others. The following functions provide a mappingbetween the frequency formats discussed above.

(hertz f [:in scale])

(keynum f [:in scale] [:from scale] [:to scale])(note f [:in scale] [:from scale] [:to scale] [:accidental acc])

(transpose f [int] [scale])

The functions hertz, keynum, and note all convert a frequency specification f

into the format for which the function is named. The function transpose shifts f

by int intervals while preserving the current frequency format of f. All fourfunctions operate with respect to a tuning system or mode optionally specified

to the function. If scale is not specified the functions will use the default scalestored in the system's global variable *scale*. By default, this variable holdsthe standard chromatic scale, whose properties are discussed in the next

Page 95: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

section of this chapter.The hertz function returns the Hertz frequency of either a key number or

note name, as shown in Interaction 10-1.

Interaction 10-1. Converting to Hertz in the standard

chromatic scale.

cm> *scale*#<tuning "chromatic-scale">cm> (hertz 'a4)440.0cm> (hertz 69)440.0cm> (hertz '(a4 c5 e))(440.0 523.251 659.255)cm>

The keynum function returns the key number of a Hertz value or note name. Hertzvalues always produce floating point key numbers, in which the fractional valuerepresents cents above the integer keynum, as shown in Interaction 10-2.

Interaction 10-2. Converting to key numbers.

cm> (keynum 'a4)69cm> (keynum 440 :hz)69.0cm> (keynum '(440 550 660) :hz)(69.0 72.863 76.019)cm>

The note function returns the note name of a key number or Hertz value.

Interaction 10-3. Converting to notes.

cm> (note 69)a4cm> (note 440 :hz)a4cm> (note '(69 72 76))(a4 c5 e5)cm>

The transpose function shifts notes or key numbers by an interval amount. If anote and typed interval are specified then the transposition will reflect theinterval's spelling, as shown in Interaction 10-4.

Interaction 10-4. Transposing notes and key numbers.

Page 96: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (transpose 69 6)75cm> (transpose 'a4 6)ef5cm> (transpose 'a4 (interval 'aug 4))ds5cm> (transpose '(a4 c5 e) (interval 'aug -2))(gf4 bff4 df5)cm>

The previous interactions point out several features common to all fourfunctions:

In order to distinguish a Hertz number from a keynum number a Hertzvalue must be “tagged” by including the keyword :hertz or :hz just afterthe value.Lists may be specified as well as single values. Lists allow sets, rows andchords to be processed in a single operation.A note in a note list may provide an octave number or not. If not, its octavenumber will default to the last specified octave number in the list, or the4th octave (middle-C octave) if no number is specified.

The Standard Chromatic Scale

The frequency functions documented in the previous section all operate withrespect to an underlying tuning system or mode. If no mode or tuning isspecified the functions use the system's default tuning object stored in theglobal variable *scale*. This scale is initially set to the standard chromaticscale but can be reset by the user. The standard chromatic scale isimplemented with 11 octaves of note entries. The first octave in the scale isnumbered -1; the Middle-C octave is 4 and the highest octave number is 10.Middle-C (written C4) is keynum 60 and has a frequency of 261.625 Hz. Thereis no upper octave limit for key numbers or Hertz values.

Note Names in the Standard Chromatic Scale

A note in the standard chromatic scale consists of a pitch class letter, anoptional accidental and an octave number:

Table 10-6. Note spellings in the standard chromatic scale.

name accidental octave

c, d, e, f, g, a, b ff, f, n, s, ss -1 to 10

Note entries include all natural (n), sharp (s), flat (f), double-sharp (ss) anddouble-flat (ff) spellings. The symbol r means rest.

Page 97: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 10-5. Note names in the chromatic scale.

cm> (keynum 'c4)60cm> (keynum 'dff4)60cm> (keynum 'bs3)60cm> (note 'c4 :accidental 's)bs3cm> (note 261.625 :hz :accidental 'ff)dff4cm> (keynum 'c-1)0cm> (hertz 'b10)31608.531cm>

Key numbers in the Standard Chromatic Scale

Key numbers in the standard chromatic scale may be expressed as integers or

a floating point numbers. A floating point key number is interpreted kkk.cc

where kkk is the integer keynum and cc is cents above the Hertz value of kkk.For example, 60.5 means 50 cents (quarter tone) above the frequency ofkeynum 60, or 269.291 Hz.

Interaction 10-6. Key numbers in the chromatic scale.

cm> (keynum 439 :hz)68.96cm> (hertz 69)440.0cm> (hertz 69.5)452.892cm> (note 69.5)bf4cm>

Intervals in the Standard Chromatic Scale

Intervals measure distances between scale degrees. In the standard chromaticscale this measurement can be expressed in terms of simple integer half steps

or by using a specially encoded typed interval that includes the quality andspelling of an interval along with its size.

Time Formats

A number of different time formats are commonly found in computercomposition: seconds, milliseconds, “ticks”, and metric proportions scaled by atempo factor. The basic format for time in Common Music is seconds. Time

Page 98: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

values can be expressed as Lisp integers, ratios and floats. The functionrhythm converts proportional and symbolic metric values to time in seconds.

(rhythm rhy [tempo] [beat])

The rhythm function converts a metric value rhy to time in seconds. rhy can be

a ratio, a rhythmic symbol (Table 10-7) or a list of the same. Tempo provides

the optional metronome speed and defaults to 60. Beat specifies themetronome's pulse and defaults to .25, or quarter note. Global defaultmetronome and beat values can be established by setting the system's*tempo* and *beat* variables, respectively.

Table 10-7. Table of Metric Symbols.

letter value letter value

m maxima q quarter

l longa e eighth

b brevis s sixteenth

w whole t thirty-second

h half x sixty-fourth

A rhythmic symbol consists of a metric letter from Table 10-7 that may beoptionally preceded by a division letter: t for triplet or q for quintuplet, andoptionally followed by any number of dots. Rhythmic symbols also supportaddition, subtraction and multiplication. The following table displays somerepresentative rhythmic symbols.

Table 10-8. Examples of rhythmic symbols.

quarter q

triplet quarter tq

dotted-eight e.

triplet dotted sixteenth ts.

triple dotted half h...

quintuplet whole qw

sixteenth plus triplet quarter s+tq

whole minus triplet sixteenthw-ts

whole times 4 w*4

Interaction 10-7. Examples of using the rhythm function.

cm> (rhythm 'q)1.0cm> (rhythm 1/4 120.0).5cm> (rhythm 'w+q )5.0

Page 99: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (define *tempo* 120.0)

cm> (rhythm '(q th e.. x) )(0.5 0.6666666666666666 0.4375 0.03125)cm>

Amplitude Formats

Amplitude as a sound property is more difficult to come to terms with thanfrequency, partly because the term 'amplitude' is used with different meanings(loudness, intensity, pressure, and so on) and partly because its perceptiondepends on a number of factors, including the frequency of the sound (theFletcher-Munson curves) as well as the listener's distance from the soundsource. When computers and amplifiers are involved the situation becomeseven more complex because amplitude can be electronically boosted orreduced at any number of points along a signal's path such that the intensitylevel that reaches our ears may have little to do with the value specified in thecomputer score! Acoustic amplitude is usually measured by pressure, or asound intensity level (SIL) as Watts per meter squared. Intensity levels are

defined from 10-12, the threshold of hearing, to 100, the threshold of pain. Since12 orders of magnitude is an enormous range of values, SIL is typically plotted

using the logarithmic Decibel scale, or dB (Table 10-9). Unfortunately, dBvalues do not really indicate how “loud” a sound seems to the listener, which isgenerally what composers are interested in. Composers typically expressamplitude in terms of a “psychological scale” represented by symbolic dynamiclevels whose meanings correspond only very loosely to the measured intensitylevels in Table 10-9.

Table 10-9. Comparison of dynamic scale to SIL. (Backus)

Dynamic level Intensity (w/m2) I/Ir SIL (dB)

ffff (pain) 100 1012 120

fff 10-2 1010 100

ff 10-3 109 90

f 10-4 108 80

mf 10-5 107 70

mp 10-6 106 60

p 10-7 105 50

pp 10-8 104 40

ppp 10-9 103 30

pppp (threshold Ir) 10-12 100 0

Since amplitude measurement is so variable, synthesis languages oftenrepresent amplitude as a normalized value between 0.0 and 1.0, where 0.0

Page 100: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

means silence and 1.0 means a fully saturated signal. The MIDI protocolrepresents amplitude values using a “key velocity” scale defined between 0 and127, where 0 is silence and 127 is maximum. Most of the examples in this bookuse the normalized representation of amplitude (0.0-1.0) so that amplitudevalues may be easily computed or controlled by envelopes and other scalingoperations. Composers who would like to work with the musical dynamic scalecan use Common Music's amplitude function to convert symbolic amplitudelevels into numerical values within a specified range:

(amplitude amp [softest] [loudest] [power])

This function converts the logical amplitude amp into a value between softestand loudest according to the power curve. A logical amplitude is a numberbetween 0.0 and 1.0 or one of the amplitude symbols shown in Table 10-10:

Table 10-10. Table of logical amplitude levels.

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

nienteppppppppp p mp mf f ff fffffff

The values of softest, loudest and power default to the value of the globalvariables *softest* *loudest* and *power*. The default power value of 1.0cause a linear increase as the logical amplitude moves from 0.0 to 1.0, asshown in Interaction 10-8.

Interaction 10-8. The amplitude function.

cm> (amplitude 'mp).5cm> (amplitude 'mp 0 127)63.5cm> (amplitude .5 0 1 4)0.0625cm>

Timbre

Timbre is such a complex sound property that there is no specific set ofparameters or data formats that control its specification. Synthesis languageslike Csound and CLM permit the composer to define the digital instruments thatproduce sound; these instruments are a strong determinant of the overallspectrum. A single sound event for one of these digital instruments may containa number of parameters that provide precise control over the timbral evolutionof the sound, by controlling the mix of overtones, or applying time varyingenvelopes to oscillators and so on. In comparison to synthesis languages, theMIDI protocol provides very little in the way of direct control over the timbralevolution of a sound. The MIDI channel parameter and the program changemessage provide the most direct means to control timbre in the synthesizerssince they determine what MIDI instruments receive the note data on each

Page 101: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 06 Oct 2003

channel.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file sound.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 102: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

11Metastructure for Composition

In Chapter 3 we learned that the property list is one way to represent thecharacteristics of an object we want to model. Example 11-1 shows how twodifferent sounds might be represented as property lists.

Example 11-1. Tone descriptions represented as property

lists.

(define piano-tone '(pitch 440 duration 1/4 loudness mf))

(define viola-tone '(pitch 440 duration 1/4 loudness mf))

Assume for the moment that the composer would like to define a function calledplay-tones to generate sound from descriptions such as those shown inExample 11-1. To the composer the two definitions represent very differentthings. But from Lisp's point of view the two lists contain the exact sameinformation and are literally equal to one another:

Interaction 11-1. Lisp cannot distinguish between the two

tones.

cm> (equal? piano-tone viola-tone)#tcm>

Since both lists contain the same information and neither list declares what

type of sound it represents, it will be impossible for play-tones to produce apiano tone from the first description and a viola tone from the second. One

possible solution to this problem might be to add a type symbol to the front ofeach list that “declares” what type of tone each list represents:

Example 11-2. Adding type symbols to the front of each

sound description.

(define sounds '((piano-tone pitch 440 duration 1/4 loudness mf )

Page 103: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(viola-tone pitch 440 duration 1/4 loudness mf ) (metal-crash duration 2 loudness ff)))

Given a list of sound descriptions such as those shown in Example 11-2 thecomposer could then design the play-tones function to check the symbol in thefirst position of each tone description to determine the action appropriate tosynthesize the sound that the symbol describes. The addition of a type symbolto the front of each list is a form of application datatyping, in which aprogrammer defines the type and format of data that will be processed by theapplication. Adding type symbols to property lists was actually used by earlyLisp researchers as a means of classifying the data that was processed bytheir programs. Most computer programs that model the real world or assisthuman activity in some way are faced with the need to represent variousobjects and behavior that make up the application's domain. The classificationscheme used by the early researchers was a primitive way of doing this withseveral serious limitations:

A classification based on type symbols is expressively very weak. While itis possible to have Piano-Tones and Viola-Tones, there is no way to

express the fact that both tones might be specializations of a moregeneral type of data called (for example) a Tone and distinguishablefrom, say, another type of sound called a Noise. The more general typesTone and Noise are completely absent from the representation.For each new type of Tone, the composer is required to add moreconditional checks and type-specific behavior to the play-tones function.This function definition will soon become an unmanageable programmingtask as the number of different sounds in the composition increases.To the underlying language the data are still just lists. It is not possible to

optimize the representation in any way.

During the 1980's Lisp adopted a powerful new methodology for modeling realworld applications. This methodology is called object-oriented programming. Inobject oriented programming a domain of interest is modeled, or represented,in terms of classes and methods that belong to an object system. TheCommon Music application is an example of a software system that representsits data and behavior using an object system. In the case of Common Music,the application domain is musical composition and the object modelinginvolves the representation of compositional structure and its behavior duringsound generation. Before turning our attention to the specifics of CommonMusic's representation, we first look at some general characteristics of objectoriented design.

Objects, Classes, Instances and Slots

An object system is a mechanism for defining the characteristics of data andthe behavior of functions that operate on that data in an application program.

An object is a software unit that represents something about the domain beingmodeled. Most object systems distinguish between two types of objects,

classes and instances. A class is an object that describes the general

Page 104: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Macro]

characteristics of the data being modeled. These characteristics aresometimes referred to as properties, slots or instance variables. We willalways use the term slot to refer to a defined characteristic. When a class isdefined it can be declared to be a subclass, or specialization, of another, more

general, class called a superclass. The superclass-to-subclass relationshipsays that every slot defined in the superclass is inherited, or automaticallypresent, in the subclass. A subclass may also declare additional slots not foundin the superclass. An instance is a specific, unique member of a class. Instanceobjects are the actual data that an object-oriented program manipulates.

Creating Instances

Every instance that a program manipulates is instantiated, or created, fromsome class description. When an instance is created it receives all of the slotsdefined by the class together with whatever default values the class declaresfor those slots. A slot's default value can be overridden by specifying a value forthat slot when the instance is created. Objects can be created and their slotsinitialized to specific values using the special macro function new:

(new class [:slot value] ...)

new creates an instance from a specification of its class and initial slot values.

Class is the class name (symbol) of the instance to create. Any number of slotinitializations may be specified following the class. Each slot initialization is apair: a keyword slot name followed by its value in the new instance. A keywordis a symbol that is prefixed with a colon. For example, creating an instance ofthe midi event class with its time slot set to .5 would look like:

(new midi :time .5)

where the symbol midi is the name of the class, :time is the keyword slot to setand .5 is its value in the new instance.

Interaction 11-2. Creating midi instances with new.

cm> (new midi)#i(midikeynum 60 duration 0.5 amplitude 64 channel 0)cm> (new midi :keynum 90 :time .5 :duration 3)#i(midi time .5 keynum 3 duration 3 amplitude 64channel 0)cm>

Common Music normally prints sound events using a specially formatteddisplay:

#i(class slot value ...)

The special prefix #i tells the user that an instance is being shown. The

Page 105: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

instance is displayed as a list containing the name of the instance's classfollowed by all its slots and values. This printing feature is a convenient way toinspect the various characteristics of a sound. The #i prefix tells you that youare looking at an instance object, not a list.

LISP Note:An #i expression may appear in upper case lettersand the order of slots may vary in different Lispimplementations.

The Common Music Class Taxonomy

There are three broad classifications of objects within the hierarchy defined byCommon Music:

Events define the characteristics of musical sounds that the composerworks with.

Containers group musical data to form larger units of structure.Processes implement procedural description of music.

Figure 11-1. A graph of Common Music's superclass->subclass hierarchy.

Page 106: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Event Classes

An event is an object that represents a sound of some kind. Common Musicallows the composer complete control over the definition of sound events butseveral specialized classes have already been defined for use with this book.The midi event class is the sound description that we will be concerned with inthe next few chapters.

The midi Event Class

The midi event is a parameterized sound description suitable for working withMIDI synthesizers, sequencing programs, and notation programs that displayMIDI data using traditional music notation.midi supports the following slot initializations:

:time numberSets the start time of the event in the score.

:keynum {keynum | note}Frequency expressed as a keynum or note. (See Chapter 14 "Tuningsand Scales")

:duration numberThe duration of the event in seconds.

:amplitude numberA logical amplitude 0.0 < 1.0 or an integer velocity 0 to 127. Defaults to64.

:channel integerA MIDI channel number 0-15. Defaults to channel 0.

Container Classes

Container classes support the aggregation of musical material. The seq,pattern, and io classes all implement different strategies for managing theobjects they contain.

The seq Container Class

A seq is a nameable object that implements musical sequencing. A seq is anameable object that facilitates the sequencing of musical objects. Theseobjects are stored as a time sorted list of subobjects. The subobject list maycontain events, processes, and other sequences. When sequences aregenerated the start times of the subobjects are always calculated relative to thestart time of the seq itself.seq supports the following slot initializations:

:time numberSets the start time of the midi event in the score.

:name {symbol | string}

Sets then name of the object to the symbol or string.

Page 107: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:subobjects listA list of subobjects.

Interaction 11-3. Creating a seq object.

cm> (new seq :name 'test :time 10 :subobjects (loop for i below 4 collect (new midi :time i)))#<seq: "test">cm> (object-time #&test)10cm> (object-name #&test)"stuff"cm> (subobjects #&test)(#i(midi time 0 keynum 60 duration 0.5 amplitude 64 channel 0) #i(midi time 1 keynum 60 duration 0.5 amplitude 64 channel 0) #i(midi time 2 keynum 60 duration 0.5 amplitude 64 channel 0) #i(midi time 3 keynum 60 duration 0.5 amplitude 64 channel 0))cm>

Common Music defines a specialized printing method for containers thatshows the name of the container when the object is printed in the Listenerwindow. A container that has no name is displayed according to whatevermethod the Lisp implementation provides.

The special prefix #& “fetches” a container given its name. This featuresaves the composer from having to define a variable just to store the objectwhen it is created.

There are a number of accessor functions that are appropriate to use withevents and containers. Consult the entry for seq in the Common MusicDictionary for more information.

Event Stream Classes

The event-stream class is the superclass for musical scores. Most scores arestored as different types of files on the computer's hard drive. Functions thatperform input or output to scores accept either event-stream instances orstrings that identify their file on the computer. Filenames that are specified toinput or output functions must always be specified with a file name extension.This extension determines the type of IO instance that will created. CommonMusic defines scores class for the following file extensions.

Table 11-1. File extensions and score types.

extensions score class

.mid, .mid midi-file-stream for MIDI

.sco sco-file-streamfor Csound

.aiff, .wav,

.snd audio-file-stream for Common Lisp Music

Page 108: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

.clm clm-file-stream for Common Lisp Music

.cmn, .epscmn-file-stream for Common MusicNotation

MIDI Scores

MIDI scores supports output to Level 0 MIDI files and input from either Level 0or Level 1 MIDI files. Level 0 means that the data in the MIDI file constitutes a

single track, or time line of MIDI events. Level 1 MIDI files contain multipletracks of data. The midi-file-stream class is automatically chosen when youspecify a file with a ".mid" or ".mid" extension.midi-file-stream supports the following slot initializations:

:versions {number | true}Causes a new version of the file to be created each time output occurs.The version name is based on the name of the file and the version count,which is initialized to number and incremented each time output occurs. Atrue value for versions is the same as 1.

:tempo numberThe MIDI tempo of the file. Defaults to 60 bpm.

:divisions integerThe number of MIDI divisions per quarter note. Defaults to 96.

:timesig (numerator denominator)The time signature for the file. Defaults to (4 4). The time signature listmay contain up to four values: (numerator denominator clocks-per-quarter32nds).

:channel-tuning {false | true | 1-16 | list}The default value of :channel-tuning is false, which means that floatingpoint key numbers are rounded to the nearest MIDI integer keynum asthey are sent to a MIDI file or port. See Chapter 15 and consult theCommon Music Dictionary for more information on :channel-tuning.

:pitch-bend-width integerDeclares the pitch bend width in semitones of the synthesizer. This valueis used to calculate pitch bend values for microtonal frequency resolution.The default value is 2, which means that the synthesizer uses a maximumpitch bend range of one whole step up and down.

Pattern Classes

The pattern class is the root class for defining musical patterns. Pattern-basedcomposition is the topic of Chapter 20.

Processes

A process is a special type of Lisp function that implements a proceduraldescription of music. Processes are introduced in Chapter 13.

Page 109: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Defining New Classes

The class hierarchy shown in Figure 11-1 is an approximation of the classesdefined in Common Music. The reason for this is that Common Music providesthe ability for composers to define their own classes of events and containers.

Composers who work with synthesis languages will typically want to definecustomized sound events appropriate for the digital instruments they are usingin the sound synthesis language. Common Music provides a macro functioncalled defobject for defining new classes of objects and for specializingexisting classes. These classes are automatically integrated into CommonMusic's score generation protocol. For more information on defobject seeChapter 24 and consult the Common Music Dictionary.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file objects.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 110: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function][Function]

Previous Contents Index Next

12Etudes, Op. 4: Score Generation

In Etudes, Op.4 we work thorough a series of examples that demonstrate howto generate scores using midi events, lists, and sequences.

Op. 4, No. 1: The Current WorkingDirectory.

All of the examples in this book generate scores that are saved as midi files onthe computer's hard drive. Although is always possible to specify a directorypath when a score is saved to a file, the examples in this book all specify onlythe name and extension of each file. A score that does not contain a directorycomponent in its file name will be written to Common Music's current “workingdirectory”. The working directory can be shown using the pwd function and setusing cd:

(pwd)(cd [dir])

The function pwd (print working directory) returns the string name of the currentworking directory. The function cd (change directory) changes the workingdirectory . If the cd is called without its optional directory argument it sets thecurrent working directory to whatever the operating system defines as theuser's “home directory”. The directory specified to cd should be a string thatends with the directory character appropriate for the host operating system. ForLinux, OS X and Windows use “/”. For MacOS use “:” as the directory delimiter.

Before attempting to generate any scores first make sure that Lisp'sworking directory is set to the directory where you would like to save scoreoutput to. Note that you must have “write permission” in order to save files inthis directory.

Interaction 12-1. Using the pwd and cd functions.

cm> (pwd)"/home/hkt/"cm> (cd "/tmp/")"/tmp/"cm> (pwd)"/tmp/"

Page 111: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

cm>

Op. 4, No. 2: The events Function

The main score output processing function is called events. This function canbe used to output events, sequences, processes and lists of these to a file,port, display or seq:

(events objects score [ahead] ...)

The events function is the main entry point for generating scores. Objects can asingle object or a list of objects. Score can be a score or a filename. Ahead is

an optional start time offset for objects in the score. If ahead is a single number

it is applied to all the objects. If ahead is a list of numbers then each number is

matched in left to right order with the corresponding object in objects. Followingthe optional offset may come any number of keyword value initializationssuitable to score. events returns the name of the score as its value.

In our first example we output a single MIDI event to a MIDI score file. Beforegenerating the file we use the pwd function to tell us what the current workingdirectory is. If you do not have write permission in the directory that is returnedby pwd then you can use cd to change to an appropriate output directory:

Interaction 12-2. Generating a MIDI event to a MIDI file.

cm> (define one (new midi :time 0 :keynum 60 :duration 2))

cm> (events one "myscore.mid")"myscore.mid"cm> (midi-file-print "myscore.mid")File: myscore.mid Format: 0Tracks: 1Division: 480

Track 0, length 20 0 #<Tempo Change 1000 ms> 0 #<Note-On 0 60 64> 960 #<Note-Off 0 60 127> 0 #<End of Track #(0)>"myscore.mid"cm>

Op. 4, No. 3: Listening to MIDI Files

In this next exercise we listen to our score. Score files can be played frominside Common Music if there is an appropriate player defined for youroperating system and audio/MIDI setup. The following MIDI file players can beused:

Page 112: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function]

OSX Note:

(osx-play-midi-file file)

plays a MIDI file using the QuickTime Playerapplication.

Linux Note:

(oss-play-midi-file file)

plays a MIDI file using the OSS utility /usr/bin/playmidi.MACOS Note:

(mac-play-midi-file file)

plays a MIDI file using the QuickTime Playerapplication.

Windows Note:

(win-play-midi-file file)

plays a MIDI file using the Windows Media Playerapplication.

These functions can be called by hand or set as output hooks that automaticallyplay score files after they are generated. In Interaction 12-3 we call the functionby hand to listen to the score.

Interaction 12-3. Listening to a MIDI file on OSX.

cm> (osx-play-midi-file "myscore.mid")"myscore.mid"cm>

→ myscore.mid

Op. 4, No. 4: File Versions and AutomaticPlayback

It is possible to generate different versions of the same basic score andautomatically play each version after it has been generated by the eventsfunction. If file versioning is activated, the file name specified to events servesas a “base name” for creating numbered versions of the same score. Forexample, if the name of the score is "myscore.mid" and file versioning isactivated, then successive calls to events will produce a sequence of filesnamed "myscore-1.mid", "myscore-2.mid", "myscore-3.mid", and so on.Versioning ensures that no score will be accidentally overwritten by the eventsfunction if it is called to generate the same score again. Versioning is also aconvenient way to compare different versions of a score as it is incrementally

Page 113: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

developed over several iterations of playback. Almost all of the file names thatappear in this book reflect file versioning. This means that as you work throughthe examples and perform your own experiments, the file names that you seereturned by the events function will not exactly match the file names printed inthis book. The function that activates and deactivates file versioning for MIDIscores is called set-midi-file-versions!:

(set-midi-file-versions! bool)

Specifying true to set-midi-file-versions! enables file versioning for MIDIscores, specifying false turns file versioning off.

Automatic playback for MIDI files can be activated by setting an output hookthat Common Music calls after the score is complete to play its file. (The term“hook” describes a Lisp function that is associated with an automatic action.)

(set-midi-output-hook! fn)

Set-midi-output-hook! establishes the function fn as a hook that called onMIDI files after they are generated by events. The hook function is passed thepathname (string) of the file and any additional hook arguments the user sets inthe file. To remove an existing output hook specify false in place of a function. Inorder to set a hook you must specify the actual function, not just its name. Thespecial Lisp prefix #' “fetches” a function object given its name. In this nextexample we activate file versioning for MIDI files, and set an output hook forautomatic score playback.

Interaction 12-4. Activating file versioning and setting anoutput hook for automatic playback.

cm> (set-midi-file-versions! true)

cm> (set-midi-output-hook! #'osx-play-midi-file)

cm>

Since these functions are used for an effect rather than for a value they do notreturn anything back to the Listener window for printing.

Op. 4, No. 5: Generating score fromMultiple Events

Multiple events can be generated to a score by specifying a list of objects to theevents function. In this next example we use a loop to define a list of MIDI notes.

Example 12-1. Definition of a list of events.

(define up (loop for key from 60 to 72 for beg from 0 by .1

Page 114: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

collect (new midi :time beg :keynum key :duration 1)))

Once the list is defined it can be passed to the events function to create ascore.

Interaction 12-5. Generating a score from a list of events.

cm> (events up "myscore.mid")"myscore-1.mid"cm>

→ myscore-1.mid

In Interaction 12-5 the variable up is defined to hold a list of midi events. The listis created using a loop that iterates two stepping variables. The first variable

key is looped over every key number from 60 (Middle-C) to the octave above.

The second variable beg starts at time 0 and is incremented by .1 secondseach time through the loop. Note that an upper bound for this second iterationclause is not necessary because the first iteration clause already includes one.

For each value of key and beg the loop collects a new MIDI event. The overalleffect of the loop is to generate a rapid, chromatic “strum” over one octave. This

is the first example in the book that demonstrates a procedural description of

musical structure. Try redefining the up variable using a loop that iterates overdifferent values for beg and key.

Op. 4, No. 5: Mixing Objects

What if we would like to hear two simultaneous strums, one up and one down?A situation like this requires that two musical gestures “overlap” in the score.This is most naturally achieved by mixing the “parallel” output from two or moresequences together. Before defining these objects we first generalize our loopinto a function that creates either an ascending or descending chromatic strumat a specified rate, amplitude and duration:

Example 12-2. Create a list of midi events between two key

numbers.

(define (strums key1 key2 rate dur amp) (let ((step (if (< key2 key1) -1 1)) (diff (abs (- key1 key2)))) (loop repeat (+ diff 1) for key from key1 by step for beg from 0 by rate collect (new midi :time beg :duration dur :amplitude amp :keynum key))))

Page 115: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The strums function creates either upward or downward strums between a

starting key1 and an ending key2. The direction of the iteration is controlled by

the variable step. The value of step is determined by an if expression that teststo see if the ending key is lower that the starting key or not. If it is then -1 is

returned and the iteration proceeds downward from key1. If key2 is greaterthan key1 than the iteration ascends from key1 by 1. The variable diff holds thenumber of chromatic steps between the two keys. The abs function is used getthe absolute value of the difference since the difference would otherwise be

negative if key2 were less than key1. The loop then repeats diff+1 times so thatthe last note will actually contain the value of key2. The rest of the loop is similarto the first example. In this next example we look at the output from a call tostrums to see the list it computes. pprint displays the list in a “pretty format”that is easy to read in the Listener window.)

Interaction 12-6. Generating strum data.

cm> (pprint (strums 60 61 .1 1 .2))(#i(midi time 0 keynum 60 duration 1 amplitude 0.2 channel 0) #i(midi time 0.1 keynum 61 duration 1 amplitude 0.2 channel 0))cm>

We next create two sequences to represent two different strums and then mixthem together to form the score.

Interaction 12-7. Creating two strum sequences.

cm> (new seq :name 'ups :subobjects (strums 48 60 .1 1 .4))#<seq "ups">cm> (new seq :name 'downs :subobjects (strums 84 72 .1 1 .2))#<seq "downs">cm> (events (list #&ups #&downs) "myscore.mid")"myscore-2.mid"cm>

→ myscore-2.mid

The list function creates a list containing the sequences named “up” and“down” that is passed to the events function. The special prefix #& “fetches” acontainer from its name.

Op. 4, No. 6: Start Time Offsets

This final example demonstrates the effect that start time offsets have onevents generated to a score. If offsets are specified they should appear directlyafter the score argument in the events call. In the first interaction of Interaction12-8 the onset of both strums is delayed by 5 seconds in the score. In thesecond interaction each strum receives a different offset, 0 for the up strum and

Page 116: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

.5 for the down. The last example shows multiple instances of the strums beingscheduled at different times.

Interaction 12-8. The effect of start time offsets.

cm> (events (list #&ups #&downs) "myscore.mid" 5)"myscore-3.mid"cm> (events (list #&ups #&downs) "myscore.mid" '(0 .5))"myscore-4.mid"cm> (events (list #&ups #&downs #&ups #&downs ) "myscore.mid" '(0 .5 1 1.5))"myscore-5.mid"cm>

→ myscore-3.mid→ myscore-4.mid→ myscore-5.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file scores.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 117: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

13Algorithms and Processes

An algorithm must be seen to be believed,and the best way to learn about what an

algorithm is all about is to try it.

— Donald Knuth, The Art of ComputerProgramming

In this chapter we begin to explore the procedural description of musicalscores, an activity that is sometimes referred to as algorithmic> composition.In this style of composition the composer writes a program to compute music.This program provides a level of abstraction above the score in which thecomposer can explore new techniques for music composition that otherwisewould be difficult or impossible to achieve. But the use of algorithms to createmusic also raises some artistic issues that should be kept in mind as youprogress through the remaining chapters of this book:

What is the relationship between a composer and a computed score? Ifthe composer cannot predict what notes will be contained in the scoreshould he or she claim authorship of the composition?When an algorithm creates a score is it really composing, does it mimiccomposition or neither? If a person cannot tell the difference by listeningto the score or by studying it, does it matter?Should a listener try to appreciate algorithmic constructions aestheticallyapart from their sound?What happens when a computer program creates the best piece on theconcert? Should the audience clap?

Before turning our attention to some specifics of algorithmic composition wefirst define what an algorithm is and how it relates to the generation of musicalmaterial. In The Art of Computer Programming (Knuth) , Donald Knuth defines

an algorithm as a set of steps, or rules, with five basic properties:

1. An algorithm must start and stop. The rules an algorithm applies mustalso conclude in a reasonable amount of time. What “reasonable” isdepends on the nature of the algorithm, but in no case can an algorithmtake an infinite amount of time to complete its task. Knuth calls this

property the finiteness of an algorithm.2. The actions that an algorithm performs cannot be open to multiple

interpretations; each step must be precise and unambiguous. Knuth

Page 118: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

terms this quality definiteness. An algorithm cannot iterate a “bunch” oftimes. The number of times must be precisely expressed, for example 2,1000000, or a randomly chosen number.

3. An algorithm starts its computation from an initial state. This state may beexpressed as input values given to the algorithm before it starts.

4. An algorithm must produces a result with a specific relation to the inputs.5. The steps an algorithm takes must be sufficiently simple that they could

be expressed “on paper”; these steps must make sense for the quantities

used. Knuth terms this property effectiveness.

These points can be illustrated using the strum procedure presented in Chapter10 and shown again here in Example 13-1 under a slightly different name.

Example 13-1. The strum1 definition.

(define (strum1 key1 key2 rate dur amp) (let ((step (if (< key2 key1) -1 1)) (diff (abs (- key2 key1)))) (loop repeat (+ diff 1) for key from key1 by step for beg from 0 by rate collect (new midi :time beg :duration dur :amplitude amp :keynum key))))

The strum1 function adheres to Knuth's five characteristics as follows:

1. Strum1 is finite because it iterates a specific number of times. The loop'srepeat clause expresses this finiteness quite succinctly.

2. Strum1 is definite because it consists of Lisp expressions in which everyquantity and relation is well defined.

3. Strum1 has five input parameters that determine the initial state of thealgorithm.

4. Strum1 produces a result, the list of musical events it returns to the caller.5. Strum1 is effective because it is able to compute its result from its inputs

and rules.

As we can see from Example 13-1, functions provide a natural mechanism forexpressing algorithms. When an algorithm is implemented as a Lisp function itis no longer just a series of steps or rules — it is a concrete Lisp object that bemanipulated and processed like all other data in the language. This means thatit is possible to test and judge an algorithm as entity in its own right. The criteriafor judging an algorithm might include its effectiveness, speed, complexity, oreven elegance of design.

Musical Processes

All of the algorithms we will explore in this book are implemented as Lisp

Page 119: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

functions. One special type of function, called a process, will be used more thanany other tool to express procedural composition. A process is a CommonMusic function that computes musical structure (sound events, sequences, even

other processes) dynamically, during the time it takes for the events function togenerating a score. A process represents the evolution of a compositional ideaover time. Processes execute in such a way that many different processes mayrun in parallel, at the same time. Each process can be thought of as an iterationthat creates a single time line in the score. This time line may co-exist with othertime lines that also exist within the same score. These “parallel” processes are

all managed by a controlling unit called the scheduler.

The Scheduler

A scheduler is like a conductor — its job is to insure that the musical objectsunder its control are evaluated in a systematic order and at their correct timeswithin a score. Scheduling is initiated by the events function when it is calledwith a list of objects to evaluate. The scheduler generates a time ordered series

of sound events from these objects by maintaining a priority queue for theirevaluation. The priority of each object is determined by the value of its time slot.If two objects have the exact same score time then the object that was placed inthe queue first has more evaluation priority than the other object at that sametime. The scheduler evaluates an object by first removing it from the head of thequeue, and then processing it in some manner appropriate to the object andscore being generated. Evaluating one object may result in the same object ornew objects being inserted into the scheduling queue with new priority times.The scheduler continues to evaluate the objects in its queue until no moreobjects remain to be processed. At that point score generation is complete, thescheduler stops, and the score is saved.

Process Evaluation

When a process function is evaluated by the scheduler the program codeexecutes. While there is no restriction on what a process function does, typicalactions include:

creating musical events and sending them to the score.creating new structures (events, sequences and processes) and addingthem to the priority queue.determining if the process should run again and, if so, determining whatits next priority time will be.

Sequence Evaluation

The scheduler evaluates a seq object by enqueing all of its subobjects into thescheduling queue. The priority time of the sequence is added to the prioritytime of each object inserted in the queue.

Event Handling

Page 120: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

When the scheduler evaluates a musical event it sends a parameterized sounddescription of that event to the score that is being created. The nature of thissound description depends both on the type of event and the type of scorebeing processed. For example, a MIDI event that is sent to a MIDI score resultsin a number of MIDI byte messages being written to a MIDI file on the disk. AMIDI event that is sent to a seq results in the event itself being inserted into theseq's list of subobjects.

Defining Musical Processes

A process function is created using the process macro function. A processdefinition looks almost identical to a loop definition except that its actionclauses implement scheduling and score actions rather than looping actions.The major difference between loop and process is that the loop macro returns

values to the caller while the process macro returns a function to evaluateinside the scheduler.

Functions as process wrappers

In this book we will always define processes inside of Lisp functions and thencall the functions to create processes. A function that contains a processdefinition can be thought of as a “wrapper” that permits the process to accessall the parameter and variable values that exist when the function is called. This

is called encapsulation and it has several important consequences:

An encapsulated process definition is a named, reusable abstraction. Itsfunction wrapper can be called any number of times in a program withouthaving to actually define the process again. Each time the function iscalled it will return a new instance of the process it encapsulates.If the wrapper function is passed arguments, different versions of thesame process description can be created simply by calling the functionwith different input values. This provides a very powerful “template”mechanism that produces variations on the process without any extrawork.

In all cases we will avoid storing processes in global variables. A process only“lives” inside the scheduler, once the scheduler has finished all of theprocesses inside it are of no more use. If a process is stored in a globalvariable then that variable will have to be set to a new process function eachtime it is used. This is much more work than simply calling a function.

Before actually learning the specifics of the process macro we will look firstat two example process descriptions.

Converting strum1 into a process

We start with an example that converts the strum1 function to (Example 13-1) toreturn a process rather than of a list of events.

Page 121: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 13-2. Converting the strum algorithm into aprocess description.

(define (strum2 key1 key2 rate dur amp) (let ((step (if (< key2 key1) -1 1)) (diff (abs (- key2 key1)))) (process repeat (+ diff 1) for key from key1 by step for beg from 0 by rate output (new midi :time beg :duration dur :amplitude amp :keynum key))))

The definition of strum2 is almost exactly the same as strum1 in Example 13-1except for three simple modifications:

1. The name of the function has been changed.2. process appears in place of loop.3. An output action appears in place of the collect action.

Interaction 13-1 demonstrates the use of strum2 to generate a score.

Interaction 13-1. Using the strum2 process.

cm> (events (strum2 48 60 .1 1 .4) "strum.mid")"strum-1.mid"cm>

→ strum-1.mid

Using now and wait

In Example 13-3 we rewrite strum2 to better reflect the dynamic nature ofprocesses:

Example 13-3. A process with changing run times.

(define (strum3 key1 key2 rate dur amp) (let ((step (if (< key2 key1) -1 1)) (diff (abs (- key2 key1)))) (process repeat (+ diff 1) for key from key1 by step for beg = (now) output (new midi :time beg :duration dur :amplitude amp :keynum key) wait rate)))

The strum3 process differs from strum2 only in the way that time is handled. In

Page 122: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

strum2 the start time of each event is determined by beg, which stepped from 0

by the rate specified to the function. This caused the time of each generatedevent to change but the time of the process itself remained unchanged. Butstrum3 presents the opposite strategy: the time of the process is incrementedand each MIDI event is output at the (current) time of the process. The functionnow returns the current priority time of the scheduler and the beg variablebecomes “time stamp” for MIDI objects sent to the score. The wait actioncauses the scheduler to reinsert the current process back into the schedulingqueue at rate seconds later than its current time.

Interaction 13-2. Listening to strum3.

cm> (events (strum3 84 72 .1 1 .4) "strum.mid")"strum-2.mid"cm> (events (list (strum3 48 60 .1 1 .4) (strum3 84 72 .1 1 .2)) "strum.mid")"strum-3.mid"cm>

→ strum-2.mid→ strum-3.mid

Note that all three strum examples we did not need to create lists of events ordefine seq objects to hold separate musical time lines. Processes can producethe same effects dynamically, while the score is being generated, without theoverhead of allocating sequences and lists ahead of time. This also means thatthe actions taken by processes can evolve over time, on the fly, in response tothe dynamic conditions of the score.

The process Macro

The process macro supports the description of iterative musical processesusing the same basic syntax as the loop macro introduced in Chapter 8. Whileboth processes and loops perform iteration they are not equivalent constructs.A loop executes immediately and returns values back to the caller. A process,on the other hand, does not iterate immediately — it returns a special type offunction that is scheduled to iterate and generate music at a later time.

A process iteration is defined as a series of clauses that only differ in thetype of actions they implement. The process macro defines a set of actions thatare specific to music composition; these clauses are documented below. Theother types of clauses — iteration, conditional, and finalization — are identicalto those provided by loop. See Chapter 8 for the definition of these clauses.

Process Action Clauses

Process action clauses implement score and scheduler tasks. Unlike loopactions, process actions never return values.

Page 123: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

[Clause]

output event [score]

wait deltasprout object [at ahead]

do expr ...

set var = value

each var {in ... | from ...} [as ...] action

The output clause sends event to score, which defaults to the score specified

to the events function. Wait sets the next priority run time of the process to deltaseconds later. The sprout clause adds object to scheduling queue. One object

or a list of objects may be specified. An optional at specifies a future ahead

time. If at is not supplied then object is inserted into the scheduling queue atthe current process time. The do clause evaluates one or more Lispexpressions and is equivalent to the identical clause in loop. The set clause

sets a variable var to value but does not actually define var inside the process.Use set to set variables that are established outside the lexical scope of theprocess but are set inside it.

The each clause defines an iteration over the action clause. It supports thesame stepping clauses that the for iteration clause supports. The differencebetween for and each is that the former defines a process iteration while eachdefines an iterative action inside the process.

Process Examples

We now look at several process definitions that demonstrate different processaction clauses. The first few examples require several of the chance and 12-tone functions implemented in Chapter 7 and Chapter 8. These are shownagain here in Example 13-4.

Example 13-4. Helper Functions from Chapter 6 and 8.

(define (chance? prob) (< (random 1.0) prob))

(define (keynum->pc k) (mod k 12))

(define (retrograde-row row) (reverse row))

(define (transpose-row row to) (loop for pc in row collect (keynum->pc (+ pc to))))

(define (invert-row row) (loop for pc in row collect (keynum->pc (- 12 pc))))

A Simple Row Process

Page 124: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

In the first example we implement a simple process to play twelve-tone rows.

Example 13-5. Basic row playing.

(define row1 '(0 1 6 7 10 11 5 4 3 9 2 8))(define row2 (invert-row row1))(define row3 (retrograde-row row1))(define row4 (retrograde-row row2))

(define (ttone1 reps row key beat amp) (process with len = (length row) repeat reps for i from 0 for p = (mod i len) for pc = (list-ref row p) ; i mod 12 for k = (+ key pc) output (new midi :time (now) :duration (* beat 2) :keynum k :amplitude amp) wait beat))

The ttone1 process plays reps number of events given a row of pitch classes, a

key number offset, a rhythmic beat and an amp loudness factor. The stepping

variable i is a counter that increases by 1 each time the process runs. Thevariable pc is set to the current pitch class at position p in row. Recall that list-ref returns an element from a list at a specified index and that the first element

in a list is always at index 0 and the last element is at index len-1. Our index into

row is determined by taking the mod 12 value of i. Although this length is always12 in our examples we have avoided specifying the value 12 directly so that it ispossible to listen to pitch class lists with more or fewer than twelve elements.

The mod calculation insures that our index p will always cycle over valid list

positions from 0 to 11 as i continually increases by 1. The key number k for the

current event is calculated by adding the value of the pitch class pc to the keyoffset that was input into the function. The duration of each event is calculated to

be 2*beat so that a legato effect is produced no matter what value of beat isspecified to ttone1.

Interaction 13-3. Listening to ttone1

cm> (events (ttone1 36 row1 60 .2 .5) "ttone.mid")"ttone-1.mid"cm>

→ ttone-1.mid

Variation 1

In this next example we make simple variation of ttone1 that plays a“pointillistic” version of a row.

Page 125: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 13-6. A pointillistic row.

(define (ttone2 len row key beat amp) (process repeat len for i from 0 for pc = (list-ref row (mod i 12)) for n = (if (chance? .5) (+ key 12) (- key 12)) for k = (+ n pc) output (new midi :time (now) :duration (* beat 2) :keynum k :amplitude amp) wait beat))

The main difference between this example and the preceding one is that

ttone2 determines a key number k by randomly choosing between values one

octave up or down from the key number input into the function. This has theeffect of slightly randomizing the register of the row so that it does not soundquite so mechanical when the row repeats over the course of the process. The

transposition value n is chosen by calling the chance? function we defined inChapter 7. The expression (if (chance? .5) (+ key 12) (- key 12)) says

that 50% of the time n will be key+12 and 50% of the time it will be key-12.

Interaction 13-4. Listening to ttone1

cm> (events (ttone2 36 row1 60 .2 .5) "ttone.mid")"ttone-2.mid"cm>

→ ttone-2.mid

Variation 2

The next variation improves on the preceding one by implementing differentwait times and controlling the amount of random pointillism that is applied to therow.

Example 13-7. A pointillistic row.

(define (ttone3 len row key beat amp) (process repeat len for i from 0 for pc = (list-ref row (mod i 12)) for w = (* beat (random 4)) for n = (if (= w 0) (if (chance? .5) (+ key 12) (- key 12)) key)

Page 126: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

for k = (+ n pc) output (new midi :time (now) :duration (* beat 2) :keynum k :amplitude amp) wait w))

In Example 13-7 the w stepping variable is set to a random multiple of beat.Recall that the expression (random 4) generates random integers 0 to 3. This

means the wait value w will be either 0 (when random returns 0), beat (when

random returns 1), 2*beat or 3*beat. A zero wait value causes the next run timeof the process to be the same as its current time in the score. This means thatthe events generated over separate iterations will sound simultaneously in thescore. The last improvement ttone3 makes is to transpose the key number onlyin the case of simultaneous notes. This has the effect of preserving some“registeral integrity” of the row's melodic contour while still providing for somevariety.

Interaction 13-5. Listening to ttone3

cm> (events (list (ttone3 36 row1 40 .2 .5) (ttone3 36 row1 60 .2 .5)) "ttone.mid" '(0 4))"ttone-3.mid"cm>

→ ttone-3.mid

Variation 3

In our final twelve-tone example we modify the process definition to improvesound characteristics in the event that simultaneous instances of the processare running.

Example 13-8. Defining sections and instrumentation.

(define (ttone4 dur row key beat amp chan) (process while (< (now) dur) for i from 0 for pc = (list-ref row (mod i 12)) for r = (* beat (random 4)) for n = (if (= r 0) (if (chance? .5) (+ key 12) (- key 12)) key) for k = (+ n pc) output (new midi :time (now) :duration (* beat 2) :keynum k :amplitude amp

Page 127: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:channel chan) wait r))

The ttone4 process makes two small modifications that greatly improve the

musicality of the output. The first modification is the addition of the new chanparameter to the function so that the process outputs to a MIDI channelspecified by the composer. MIDI channels control which instrument in thesynthesizer receives the note on and off messages. When you work with MIDIchannels, remember that indexing always starts from 0 in Lisp so MIDI channelsin Common Music are numbered from 0 to 15. Since our numbering system iszero-based this allows channel values to be calculated without constantlyadding 1 to them.

The second modification to the process alters the way in which thealgorithm controls iteration. In all of the preceding examples the processesiterate an exact number of times. In this version, the process iterates for a

specified duration of time, regardless of how may iterations the processexecutes. The conditional clause

while (< (now) dur)

says to run the process as long as the current time of the process is less thanthe duration specified to the function. As soon as this condition is false thescheduler automatically stops the process.

We now listen to three contrapuntal voices of the same basic processdefinition, where each version has a different row form, register, instrument,start time and duration in the score. All three processes stop generating eventsat the same time in the score. Before playing the example be sure to selectdifferent MIDI instruments on the first three channels of your synthesizer. Listento several versions of the example to observe the large scale effects thatrandom selection has on each process.

Interaction 13-6. Playing a little 12-tone etude.

cm> (events (list (ttone4 16 row1 40 .2 .5 0) (ttone4 12 row2 60 .2 .5 1) (ttone4 8 row3 80 .2 .5 2)) "ttone.mid" '(0 4 8))"ttone-4.mid"cm>

→ ttone-4.mid

Ghosts

As a final example of algorithmic process definition we look at a more involveddefinition called ghosts, written by Tobias Kunze. In addition to its interestingmusical properties ghosts also provides examples of most of the actionclauses supported by the process macro. The definition of ghosts requiresseveral “helper functions” introduced in Chapter 7. The definitions of these

Page 128: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

auxiliary functions are shown again here in Example 13-9.

Example 13-9. Helper functions for ghosts.

(define (pick-list lst) (let* ((len (length lst)) (pos (random len))) (list-ref lst pos)))

(define (pick-range low high) (let ((rng (- high low))) (if (> rng 0) (+ low (random rng)) 0)))

(define (bpm->seconds bpm) (/ 60.0 bpm))

(define (rhythm->seconds rhy tempo) (* rhy 4.0 (bpm->seconds tempo)))

The ghosts process gets its name from the fact that a short, randomlygenerated melody serves as the basis for computing other bits of dependentstructure that echo the melody in different ways at larger temporal levels in thepiece. ghosts generates the following gestures, each of which depends oncertain tests applied to the characteristics of the main melody:

Temporally stretched versions of some melodic tones are repeated highabove the the main melody.Percussive thumps sometimes accompany the main melody in the lowregister.Distant strums accompany the high stretched melody at even larger timescales.

Before looking at the main process we first define the functions that create thedependent gestures used in ghosts.

Example 13-10. Defining gestures for ghosts.

(define (hitone knum at) ;; create a long tone two octave above knum (new midi :time at :keynum (+ knum 24) :duration at :amplitude .5))

(define (thump knum at) ;; make two percussive events below knum (list (new midi :time at :keynum (- knum 18) :duration .05 :amplitude .4) (new midi :time at :keynum (- knum 23) :duration .05 :amplitude .4)))

Page 129: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define (riff knum rhy) ;; generate an upward strum of notes (let ((rate (rhythm->seconds (/ rhy 4) 60))) (process repeat 5 for k from (+ 39 (mod knum 13)) by 13 output (new midi :time (now) :keynum k :amplitude .3 :duration 10) wait rate)))

The hitone function creates a single midi event two octaves (twenty-foursemitones) above the knum specified to the function. The at parameter servesas both the start time of the new midi event as well as its duration! The effect ofthis is to create longer and longer high tones as the ghost process incrementstime.

The thump function returns a list of two midi notes, each with a very shortduration. knum is the current main melody note and at is the time at which thethump events are to occur in the score. The key number of the first thump eventis always an octave and a tritone lower than the main melody note; the secondevent is a perfect fourth above that. Since these transpositions are a fixedrelationship, the thump events will follow the general melodic contour of themain melody notes to which they are attached.

The riff function defines a process that outputs five rapid events in anupward arc. knum is the current main melody note and rhy is its metric rhythmvalue. riff converts this metric rhythm into a wait value that is four times fasterthen the main melody rhythm. The first event that riff plays is a transposition ofthe main melody key number shifted to lie somewhere within the octave abovekey number 39 (E-flat in the second octave). Each of the remaining four eventswill have a key number one minor-9th above the previous event.

We can listen to the gestures of thump and riff in isolation beforeproceeding to the main ghosts process definition:

Interaction 13-7. A sample thump and strum.

cm> (events (hitone 60 0) "thump.mid")"thump-1.mid"cm> (events (thump 60 0) "thump.mid")"thump-2.mid"cm> (events (riff 60 1/8) "riff.mid")"riff-1.mid"cm>

→ thump-1.mid→ thump-2.mid→ riff-1.mid

The ghosts process generates a main melody in the middle register of the MIDI

keyboard and then sprouts dependent structure above and below the mainmelody based on tests it applies to the melodic material. Recall that a sproutclause inserts objects into the scheduling queue at a future time. By using

Page 130: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

sprout instead of output the dependent structures that ghosts generatesreflect current conditions of the process that are actually heard at a much laterpoint in the score.

Example 13-11. The ghosts process.

(define (ghosts) (process repeat 12 for here = (now) for ahead = (* (+ here .5) 2) for main = (pick-range 53 77) for high? = (>= main 65) for amp = (if high? .6 .4) for rhy = (pick-list '(1/16 1/8 3/16)) ;; output main melody output (new midi :time here :keynum main :duration (rhythm->seconds rhy 60 ) :amplitude amp) when high? sprout (hitone main ahead) and sprout (riff main rhy) at (* ahead 2) when (= rhy 3/16) sprout (thump main (+ here .5)) wait (rhythm->seconds rhy 60 )))

The here variable holds the current score time returned by now. Ahead is afuture time twice the value of one-half second later than the current time in the

score. In other words, as here increases each time the process runs the valueof ahead is roughly twice as far in the future. Main is the main key number ofthe melody randomly set to any key number within a two octave range above

key number 53 (F below Middle C). The high? variable is set to either true orfalse based on a test of the main key number. Recall that arithmetic relationslike = are functions that return either true or false. The expression (>= main 65)

returns boolean true if the main key number is greater than or equal to 65otherwise it returns false. So the high? variable will be true if the main keynumber was selected from the upper octave in the key number range, otherwisehigh? will be false. If high? is true then the main melody is played with at anamplitude of .6 otherwise it is played at the softer level of .4.

The last stepping clause sets the rhythm rhy of main melody> to a randomchoice of either a sixteenth, eight or dotted-eighth value.

The first action clause that ghosts performs is to output a main melody midi

event at the current time in the score. The following clause is a conditionalclause that executes two sprout actions only if the high? variable is true. Theconditional expression is reduced to one line here to show its important clausalfeatures:

when high? sprout (hitone ...) and sprout (riff ...)

A when clause performs a test and evaluates one or more dependent actionclauses only if the test is true. The and conjunction joins action clauses togetherthat depend on a single test. The conditional clause in ghosts has the effect of

Page 131: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

generating a high melodic tone 24 semitones above the main tone and a riffgesture only if the current main tone lies within the upper octave of its register.

The next clause in the process is another conditional clause but this timewith only one dependent action:

when (= high 3/16) sprout (thump ...)

The effect of this clause is to only create a thump gesture if the current value ofrhy is 3/16, i.e. a dotted-eighth value.

The last clause in ghosts is a wait action clause that increments the priority

time of the process by the current rhythmic value of rhy converted to seconds.Since there is no and conjunction preceding the wait clause it is independent ofthe preceding conditional clause, which means that it is evaluated every timethe process runs.

We are now ready to listen to ghosts. Generate the score a number ofdifferent times to hear how the non-random dependent gestures track therandomly generated main melody.

Interaction 13-8. Listening to ghosts.

cm> (events (ghosts ) "ghosts.mid")"ghosts-1.mid"cm> (events (ghosts ) "ghosts.mid")"ghosts-2.mid"cm>

→ ghosts-1.mid→ ghosts-2.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file processes.cm located in the same directory as the HTML filefor this chapter. The source file can be edited in a text editor or evaluatedinside the Common Music application.

References

Knuth, D.E. (1981). The Art of Computer Programming. Reading: Addison-Wesley.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 132: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

14Etudes, Op. 5: Steve Reich's PianoPhase

In Etudes Op. 5 we model some of the basic algorithmic features of SteveReich's composition Piano Phase. The purpose of this Etude is not to recreatea complete and faithful rendition of Reich's composition, but rather, to try tounderstand some of the issues involved with his notion of phasing, and to seehow these issues might be represented in the metalevel. Once our chapterimplementation is complete the reader may use it to experiment with differentphasing materials or to extend and enhance its basic functionality.

The Composition

Steve Reich's Piano Phase is a composition for two pianists in which a singlegroup of notes — called the trope in this Etude — is repeated over and overagain for the entire length of the composition. The piece is laid out as a seriesof sections; the exact length of each section (measured in repetitions of thetrope) is left up to the performers. The piece begins with the first pianist playingthe trope at a constant tempo. The first pianist must maintain this tempo strictlyfor the entire composition. After several repetitions of the material the secondpianist joins at the unison and remains “in phase” for several repetitions of thetrope. At this point the second pianist gradually speeds up over the course ofseveral more repetitions of the trope, until piano two is playing the trope onenote ahead of the first performer. Once this has occurred, the second pianistresumes a constant tempo so that the two performers play in lock step again(but now one note out of phase). The entire process is repeated again andagain until the second performer has finally moved ahead through all of thenotes in the trope. In the final section of the composition both performers areonce again playing in unison at an identical constant rate. The reprinted score

and directions to Piano Phase can be found on page 554 of Tonal Harmony, byKosta and Payne. (Kosta)

The Implementation

Our implementation of Piano Phase will provide the basic looping and phasingfeatures of the piece but without implementing random section lengths or a

Page 133: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

slowly increasing tempo. (These improvements will be left to the reader todesign once pattern-based composition and linear interpolation have beencovered in later chapters of this book.) Our first exercise in this Etude will be toimplement the musical process that represents Piano 1. Since this performerplays the trope without any change for the length of the composition, the basicprocess definition for piano1 is rather straightforward, as shown in Example 14-1.

Opus 5, No. 1: Piano 1

Example 14-1. Helper functions and Piano 1.

(define phasing-trope (keynum '(e4 fs b cs5 d fs4 e cs5 b4 fs d5 cs5)))

(define phasing-pulse 1/24)

(define phasing-tempo 72)

(define (bpm->seconds bpm) (/ 60.0 bpm))

(define (rhythm->seconds rhy tempo) (* rhy 4.0 (bpm->seconds tempo)))

(define (piano1 trope amp chan) (let* ((tlen (length trope)) (cycs tlen) (rate (rhythm->seconds phasing-pulse phasing-tempo))) (process repeat (* tlen cycs) for i from 0 for x = (mod i tlen) for k = (list-ref trope x) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude amp :channel chan) wait rate)))

The piano1 process repeats a trope of key numbers for as many times as thereare key numbers in the trope. The total number of events that the process playsis equal the trope's length tlen times cycs, the number of times the trope is

repeated and the variable i counts each event the process is plays. Thevariable x is the index of the current key number to play in the trope. X is set to i

mod tlen so that x continually cycles from zero to tlen-1 as i increases by oneeach time the process runs. The rate at which the process plays is determined

by multiplying the global pulse value phasing-pulse by the global tempo factorphasing-tempo.

In Interaction 14-1 we listen to several repetitions of the trope played bypiano1.

Page 134: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 14-1. Listening to 12 repetitions of phasing-trope.

cm> (events (piano1 phasing-trope .5 0) "reich.mid")"reich-1.mid"cm>

→ reich-1.mid

Opus 5, No. 2: Piano 2

The function that represents the second performer is responsible for producingthe regular pattern of phasing that occurs throughout the piece. The phasingpattern consists of two parts. The first part maintains a constant rate of eventsat the identical speed as piano1. The second part of the pattern is slightly fasterand causes piano2 to slowly move one note ahead of piano1. At this point thephasing pattern over with the constant rate again. We The phasing pattern will

be determined by a tempo curve, a list of tempo coefficients that produce adynamically changing rate in the piano2 process. The coefficients in the firsthalf of the tempo curve will consists of 1's, which cause the rate of the processto remain constant. The coefficients in the second half of the tempo curve willconsist of values slightly less than 1 — the smaller coefficients cause the valueof rate to become shorter and thereby increase the speed at which the piano2process plays the trope.

The phasing coefficients can be represented as the ratio p/n where p is the

number of pulses the phasing takes and n is the number of events to play in thattime. For example, a p/n of 16/17 would mean to play 17 notes in the time of 16pulses. If we assume that each half of the phasing equals one repetition of thetrope, then the p/n ratio for our version of Piano Phase would be 12/13, where12 is the length of Reich's trope and n is one more than that value. That extra nvalue is the note that the performer moves ahead by. We now implement afunction that creates a tempo curve (Example 14-2).

Example 14-2. The phasing-tempo-curve definition.

(define (phasing-tempo-curve tlen stay move) (let* ((p (* tlen move)) (n (+ 1 (* tlen move))) (l1 (loop repeat (* tlen stay) collect 1)) (l2 (loop repeat (+ 1 (* tlen move)) collect (/ p n)))) (append l1 l2)))

phasing-tempo-curve returns a list of tempo coefficients for scaling the rate ofevents that piano2 produces. Tlen is the length of the trope. Stay is the number

of repetitions of the trope in which piano2 is synchronized with piano1. Move isthe number of repetitions of the trope during which piano2 plays faster than

piano1. The p and n values are calculated from these repetition values times

the length of the trope. The l1 variable holds the list of tempo values for the

Page 135: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

constant half of the phasing pattern, in which the coefficients are all 1. The l2

variable holds the list of the p/n tempo values for the second half of the phasingthat produces the faster tempo. Since piano2 must move ahead by one note,the second half of the tempo curve list contains 1 more element than the firsthalf. The entire tempo curve is created by appending the two halves of thepattern together to form one large list that is returned as the value of thefunction. We can test the tempo curve function with input values to see what itproduces (Interaction 14-2).

Interaction 14-2. Creating a tempo curve list.

cm> (phasing-tempo-curve 3 1 1)(1 1 1 0.75 0.75 0.75 0.75)cm>

The first half of the tempo curve contains three 1's which add up to 3 beats. Thesecond half of the tempo curve list contains four .75 values which also add up to3 beats. The total duration of the phasing sequence is 6 beats, so this tempocurve process would play seven events in the same time that a constantprocess at tempo 1 played 6. We are now ready to implement piano2(Example 14-3).

Example 14-3. The piano2 process.

(define (piano2 trope stay move amp chan) (let* ((tlen (length trope)) (cycs tlen) (coda (* stay tlen)) ; ends with unison segment (curve (phasing-tempo-curve tlen stay move)) (clen (length curve)) (rate (rhythm->seconds phasing-pulse phasing-tempo))) (process repeat (+ (* clen cycs) coda) for i from 0 for k = (list-ref trope (mod i tlen)) for c = (list-ref curve (mod i clen)) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude amp :channel chan) wait (* rate c))))

Trope is the list of key numbers to play. Stay and move are the two parts of thephasing pattern — stay is the number of repetitions of trope during which

piano2 remains at the constant tempo, move is the number of repetitions of

trope during which the tempo is increased. Tlen and cycs are exactly the same

as in piano1. Coda is the number of events to play at the very end of the piece,after cycs number of pattern cycles has occurred and the entire process has

moved through all the notes of the trope. The extra coda amount allows bothpianos to conclude the piece by playing the constant portion of the pattern in

Page 136: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

unison again, just as the piece began. Curve is the tempo curve and clen is itslength, which is also the total number of notes in the phasing pattern.

The body of the process definition is very much like piano1, except thatpiano2 gets its current tempo coefficient c from the tempo curve. This

coefficient is then used to scale the rate value in the wait clause.

Interaction 14-3. Listening to repeats of phase pattern.

cm> (events (piano2 phasing-trope 1 1 .5 0) "reich.mid")"reich-2.mid"cm>

→ reich-2.mid

Opus 5, No. 3: The final version

All that remains in our project is to define a wrapper function called piano-phase that creates both processes. We will also reimplement our piano1

function so that it also accepts the stay and move repetition factors thatdetermine the length phasing pattern. The stay and move parameters will allowus to experiment with different phasing proportions without having to redefinethe functions each time (Example 14-4).

Example 14-4. The final version of piano1 and the pphase

function.

(define (piano1 trope stay move amp chan) (let* ((tlen (length trope)) (cycl tlen) (reps (* tlen (+ stay move) cycl)) (coda (* stay tlen)) (rate (rhythm->seconds phasing-pulse phasing-tempo))) (process repeat (+ reps coda) for i from 0 for x = (mod i tlen) for k = (list-ref trope x) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude amp :channel chan) wait rate)))

(define (pphase trope stay move amp) (list (piano1 trope stay move amp 0) (piano2 trope stay move amp 1)))

Before listening to the phasing examples make sure that you have pianosounds selected on the first two channels of your synthesizer. The first inputexpression in Interaction 14-4 creates a score with stay and move both set to1. This produces a very short version of the piece (about 40 seconds) in which

Page 137: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

the short phasing sections sound more like cross rhythms than they do actualphasing. The second score keeps stay at 1 repetition but increases move to 8,which allows the phasing sections to evolve at a more realistic rate of eightcycles of the trope. This second version of the piece lasts 3 minutes.

Interaction 14-4. Listening to pphase.

cm> (events (pphase phasing-trope 1 1 .5) "reich.mid")"reich-3.mid"cm> (events (pphase phasing-trope 1 8 .5) "reich.mid")"reich-4.mid"cm>

→ reich-3.mid→ reich-4.mid

Try experimenting with phasing using shorter or longer tropes as well asdifferent stay and move values.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file reich.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

References

Kosta, S., & Payne, D. (2000). Tonal Harmony. Boston: McGraw Hill.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 138: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

15Microtonality, Tunings and Modes

There is still a lot of good music waiting tobe written in C major.

— Arnold Schönberg

One of the most liberating aspects about using a computer to compose musicis that non-traditional “frequency space” can be explored in different wayswithout having to address the limitations of physical instruments or humanperformers. This sort of experimentation may involve working with raw,untempered Hertz values, just harmonic ratios, microtonal inflections, or“alternate” tuning systems, which we define very broadly to mean any tuningsystem other than the standard 12-tone equal tempered scale. But working withan expanded frequency space using MIDI synthesizers will quickly expose astrong bias of MIDI towards the popular music tradition and the Western tonalsystem in general. Nowhere is this bias more pronounced than in MIDI's

implementation of key number frequency control, which quantizes an infinitenumber of frequencies into just 128 scale degrees that represent the Westernkeyboard instrument. As a result, composers working outside the popular,mainstream tradition (or even outside the popular keyboard tradition!) must findways to overcome a tendency of MIDI to generate sounds based on thestandard twelve note division of the equal-tempered chromatic scale.

MIDI and Microtonal Tuning

There are two basic methods for producing microtonal sound on a MIDIsynthesizer. The first method is to retune the synthesizer “by hand” and thenactivate the retuned sounds using normal integer key numbers. The advantagewith this approach is that the stream of messages between the computer andsynthesizer does not change, i.e. nothing extra needs to be done in order toproduce microtones. But there are some disadvantages with this method aswell:

1. since tuning methods vary with each synthesizer they cannot be explainedin a general way in a book

2. specific retunings may not be supported by a given synthesizer3. retuning the synthesizer often affects all the sounds in the synthesizer4. retuning may be difficult or time consuming to do by hand

Page 139: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

A second, alternate approach for producing microtones on a MIDI synthesizeris to include specific retuning instructions in the stream of messages that issent to the synthesizer. We will refer to this type of tuning as channel tuningbecause it only affects sounds on specific channels of the synthesizer. The MIDIinstructions that produce these microtonal frequency adjustments are called

pitch bend messages. The effect of these messages can be heard by movingthe synthesizer's pitch bend wheel up or down while a tone is sounding. Theresulting effect is a smooth glissando moving upward or downward from theoriginal frequency. On most synthesizers the maximum pitch bend width is setto a major second on either side of a given key number. This value is importantto know because the tuning instructions sent to the synthesizer are defined as a“percentage” of this value. If the maximum pitch bend value on your synthesizeris not a major second, consult the explanation of :pitch-bend-width in theCommon Music dictionary.

Channel Tuning

Channel tuning produces microtonal inflections by calculating specific pitchbend adjustments that retune the closest integer key number to the desiredfrequency. The pitch bend instruction is then sent to the synthesizer immediatelybefore the the integer key number is sent so that when the sound starts it isalready tuned to the correct tone. The advantages that channel tuning has overretuning the synthesizer by hand is that the retuning happens dynamically andthat these adjustments can produce almost any untempered frequency, not justthose the composer initializes in the sequencer beforehand. The disadvantagewith channel tuning is that MIDI pitch bends are will affect all sounds on aparticular MIDI channel, not just specific key numbers. This means that if asound is currently playing on the same channel to which a pitch bend is sent, theexisting sound will be affected as well as the tone for which the pitch bend isintended. The basic strategy for channel tuning, then, is to send bend and keynumber pairs to channels that are currently silent. This generally requires thatthe composer reserves some range of channels for the software to use as its“pool” of available channels for retuning.

Note by note tuning

There are several different strategies for managing this pool of tuning channels.In note-by-note tuning each pitch bend and key number pair is sent to the nextavailable channel in the pool. The selection of this channel can be made eitherby maintaining a queue of available “open” channels (channels that are notcurrently sounding a note) or simply by cycling thorough a sufficiently largenumber of channels such that no overlapping notes in any channel will beproduced. Both queuing and cycling methods send an exact microtonal pitchbend adjustment for each note. The disadvantage with both methods is that it ispossible to run out of available channels if there are too many overlappingdurations in the MIDI data.

Equal division tuning

Page 140: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

An alternate method for implementing channel tuning is to quantize microtonesto a sufficiently small division of the semitone such that one gets “close enough”to the desired microtonal inflection. These divisions are then“spread” over arange of channels that are pretuned to produce the desired quantization size.This means that pitch bends do not need to be sent on each note and so a pitchbend cannot incorrectly affect overlapping sounds. The disadvantage, ofcourse, is that the quantization size may be too “corse” and adversely affect theperception of microtonal inflections. The minimum possible quantization stepsize in Common Music is 6.25 cents, very close to the smallest perceivablechange in frequency by humans. Only experimentation can determine the bestchannel tuning strategy for a given situation. If quantization is not an issue — forexample the microtones are equal tempered, or the texture is sufficiently denseso that it does not matter — then the quantization is inherently more efficientand less problematic than the note-by-note channel tuning method.

Channel Tuning in Common Music

Common Music supports both note-by-note and equal division styles of channeltuning. Either method can be specified using the :channel-tuning keywordargument to the events function. Most of the examples in this chapter use thequantization method in which each semitone is divided into 9 equal divisions,or approximately 11 cents per quantization step. Our channel tuning will use thefirst nine channels of the synthesizer in order to avoid the drum track that ishardwired in channel 9 (zero-based) on many synthesizers. You shouldexperiment with different forms of channel tuning to compare the results. Thedocumentation on MIDI files in the Common Music Dictionary containscomplete information on the :channel-tuning initialization. This documentationshould be reviewed before beginning to work with the examples.

Listening to the Harmonic Series

In Chapter 10 we learned that many musical instruments produce complex

waves whose frequencies are related by the harmonic series (Figure 15-1).The harmonic series can be generated from any frequency f by simple

multiplication: f*1, f*2, f*3...f*n. A loop can be used to generate this series

based on any fundamental f:

Interaction 15-1. Computing a harmonic series based on

100 Hz.

cm> (loop with f = 100 for h from 1 to 12 collect (* f h))(100 200 300 400 500 600 700 800 900 1000 1100 1200)cm>

Similarly, we can also define a process to play any segment of the harmonicseries between a lower and upper harmonic number:

Page 141: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 15-1. Playing the harmonic series.

(define (play-harms frq low high rate) (process for h from low to high for f = (* frq (/ h low)) for k = (keynum f :hz) output (new midi :time (now) :keynum k :duration (* rate .9)) wait rate))

The play-harms process produces tones in the harmonic series between a low

and high harmonic number based on a low frequency frq. The frequency f of

each harmonic h is determined by multiplying frq by the ratio h/low such that theseries starts with frq as the low harmonic. The keynum function is used to

produce a floating point MIDI key number k for each calculated Hertz value f.

The :hz keyword after h in the argument list tells keynum that f is a Hertznumber.

We can now listen to the harmonic series using several different channeltuning methods (Example 15-2).

Interaction 15-2. Playing the harmonic series.

cm> (events (play-harms 440 8 16 .4) "micro.mid" :channel-tuning true)"micro-1.mid"cm> (events (play-harms 440 8 16 .4) "micro.mid" :channel-tuning 9)"micro-2.mid"cm> (events (play-harms 440 8 16 .4) "micro.mid" :channel-tuning false)"micro-3.mid"cm>

→ micro-1.mid→ micro-2.mid→ micro-3.mid

The last expression in Interaction 15-2 “turns off” channel tuning. As an alternatemethod for canceling pitch bend effect one could also define a process thatsends a series of level 0 pitch bends to every channel to reset the synthesizer toits “normal” state, as shown in Example 15-2.

Example 15-2. Clearing pitchbends.

(define (clearbends ) (process for c below 16 output (new midimsg :time (now) :msg (make-pitch-bend c 0))))

One of the most interesting properties of the harmonic series is its fractal

Page 142: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

nature: each prime number in the series introduces a new (non-doubling) tonethat then becomes a new “fundamental” for generating another harmonic series.For example, the third harmonic in Interaction 15-0 at 300 Hz is the firstfrequency in the series that is not a doubling (octave) of the fundamental and itgenerates its own series: 300, 600, 900, 1200. Similarly, the 5th harmonicgenerates the series 500, 1000, 1500, and so on.

Another interesting aspect of the harmonic series is that its harmonic“density” rises with the harmonic number. Since the harmonic series is linearbut octaves are powers of two, there are more and more harmonics per octaveas the series rises: the first octave in the series contains 1 frequency, thesecond 2, the third 4, the fourth 8 and so on. This increasing harmonic densitydoes not produce much harshness in the timbre of physical instrumentsbecause the amplitudes of the higher harmonics tend to fall off rapidly with theharmonic number. This means that the effect of the higher harmonics quickly

becomes masked by the louder, more widely spaced frequencies lower in thespectrum (Figure 15-1).

Figure 15-1. The first 32 harmonics in the harmonic series

based on a fundamental of 100 Hz. The amplitude of each

harmonic has been calculated as the reciprocal of theharmonic number, or 1/h.

But using the computer we can listen to the relationship between higherharmonics by defining a process that play multiple octaves in the series“wrapped” to fit within a single octave:

Example 15-3. Playing the harmonic series.

(define (play-harms2 octs) (process with hz = (hertz 'c3) for h from 1 to (* 12 octs) for k = (keynum (* h hz) :hz) output (new midi time (now) duration 1.0 amplitude (between 0.4 0.6) keynum (+ 48 (mod k 12))) wait .15))

The play-harms2 process plays the harmonic series based on the hertz valueC3 in the standard chromatic scale. The series is produced modulo the octave

Page 143: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(12 semitones) so that consecutive octaves in the series start over again on thesame bottom tone. Notice that each time the octave starts over the number ofdivisions in the octave doubles.

Interaction 15-3. Playing four octaves of a normalized

harmonic series.

cm> (events (play-harms2 4) "harms.mid" :channel-tuning 9)"harms-1.mid"cm> (events (clearbends ) "clearbends.mid")"clearbends-1.mid"cm>

→ harms-1.mid→ clearbends-1.mid

Intervals in the Harmonic Series

Since each ascending octave in the series contains more frequencies, it standsto reason that the intervals between consecutive harmonics in the seriesdecrease as the harmonic number increases. Musically speaking, theseintervals are a difference — the space between two tones. But physicallyspeaking, an interval is a proportion, or ratio, between two frequencies.Ascending or descending intervals between any two tones in the harmonicseries can be determined by the ratio of their harmonics numbers. The order inwhich the ratio is expressed does not matter — if the ratio is larger than 1 thenan ascending interval is described, if the ratio is less than 1 then the interval isdownward. For example, a 3/2 ratio produces is a perfect fifth up and 2/3 is aperfect fifth down. The 3/2 perfect fifth is called just interval because it is foundin the harmonic series, i.e. it is a ratio of two whole numbers. In contrast, the

ratio for an equal-tempered perfect 5th is 27/12, or 1.4983070768766815,which is very close to, but not equal to 3/2. Ratios for some common justmusical intervals are shown in Table 15-1.

Table 15-1. Ratio and cent values of common just

intervals.

interval ratio cents

unison 1/1 0

minor 2nd 16/15 112

major 2nd 9/8 204

minor 3rd 6/5 316

major 3rd 5/4 386

perfect 4th 4/3 498

tritone 45/32 590

interval ratio cents

perfect 5th 3/2 702

minor 6th 8/5 814

major 6th 5/3 884

minor 7th 9/5 1018

major 7th 15/8 1088

octave 2/1 1200

Page 144: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

Interval Math

Since intervals are ratios they must be “added” using multiplication and division

rather than addition and subtraction. For example, to add the intervals of a justfifth and fourth, multiply their ratios together:

(3/2 * 4/3) = 12/6 = 2/1

Similarly, the “subtraction” of minor third from an octave requires division:

(2/1 / 6/5) = (2/1 * 5/6) = 10/6 = 5/3

To “invert” a perfect fifth means to take its reciprocal by dividing into 1.

(1/1 / 3/2) = (1/1 * 2/3) = 2/3

To find the “complement” of a ratio, multiply the smaller of the numerator ordenominator by 2. If the numerator is smaller then multiply by 2/1, if thedenominator is smaller multiply by 1/2. For example, to find the complement of3/2 multiply by 1/2 to produce the perfect fourth:

(3/2 * 1/2) = 3/4 = 4/3

and a just major sixth is the complement of a minor third:

(5/6 * 2/1) = 10/6 = 5/3 = 3/5

Cents

One problem with using ratios to describe intervals is that the size of a ratio can

be non-intuitive. For example, which is larger: 256/243 or 9/8? The cent scalewas devised as alternative to ratios for describing the distance between twofrequencies. By definition, there are 100 cents in a tempered semitone and1200 cents per octave. This means that one cent is 1/1200 of an octave — animperceptibly small change in frequency. Since the smallest frequencydifference humans can perceive is approximately 5 cents, the cent unit is morethan adequate for describing minute changes in frequency. Cen values arelinear and can be combined using simple addition and subtraction. Forexample, the size of just perfect fifth can be determined by adding the centvalues of a just major third and a just minor third: 316+386=702 cents. CommonMusic provides two functions that convert between cents and ratio scalers:

(cents->scaler cents)

(scaler->cents ratio)

The cents->scaler function converts cents into the scaler value 2cents/1200

while the scaler->cents function performs the reverse calculation

1200*log2ratio.

Interaction 15-4. Converting cents to scaler values.

Page 145: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (cents->scaler 1200)2.0cm> (scaler->cents 3/2)702cm> (scaler->cents 5/4)386cm> (scaler->cents 6/5)316cm> (cents->scaler 700)1.4983070768766815cm>

Listening to Microtonal Intervals

We can define functions that create MIDI events to play and comparemicrotonal intervals:

Example 15-4. Playing microtonal intervals.

(define (midi-ratio at note ratio dur) (let ((fkey (keynum (* (hertz note) ratio) :hz))) (list (new midi :time at :keynum note :duration dur) (new midi :time at :keynum fkey :duration dur))))

(define (compare-ratios note rat1 rat2 dur) (append (midi-ratio 0 note rat1 dur) (midi-ratio (+ dur .5) note rat2 dur)))

(define ct8 (list true 1 9))

The midi-ratio function returns two midi events whose key numbers are ratiointerval apart. The first event in the list plays the specified note in the standard

chromatic scale. The second event plays a tone at fkey calculated to be ratio

distance above or below note. This value is determined by multiplying the hertz

value of note by ratio and then converting the value to a floating point keynumber in the chromatic scale.

The second function compare-ratios allows us to compare two differentintervals. This function simply appends the results of two calls to midi-ratio.The second interval will be played .5 seconds after the first interval ends. Thect8 variable is :channel-tuning value for producing note-by-note tuning overchannels 1-8. Channel 0 remains free of pitch bends and so the the first tone ofeach interval will be tuned to the standard chromatic scale.

Interaction 15-5. Playing and comparing ratios.

cm> (events (midi-ratio 0 'c4 5/4 3) "rat.mid" :channel-tuning true)"rat-1.mid"cm> (events (compare-ratios 'c4 5/4 (expt 2 4/12) 3) "rat.mid" :channel-tuning ct8)"rat-2.mid"

Page 146: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Class]

cm>

→ rat-1.mid→ rat-2.mid

The first input produces a just major third. The second example compares the

just major third with the equal tempered major third, or 24/12. The just major thirdis noticeably smaller at 386 cents #&8212; 24 cents less than the equaltempered version.

Scales and Temperament

A musical scale quantizes infinite frequency space into a finite number of steps,

called scale degrees. Scale degrees serve as the raw material for acomposition and are generally ordered low to high within a given scale. Theterm temperament refers to the precise relationships between the ordered

degrees in a scale, and a tuning system, describes a formal method fortempering each scale degree.

Scales and Tunings In Common Music

A scale in Common Music is an object that defines an order series of scaledegrees. Each scale degree is represented by an integer position in the scale

called a keynum, a hertz value, and possibly one or more note names

associated with each degree. The term scale in Common Music actuallyapplies to two different types of objects. A tuning is a scale that defines theprecise frequencies for between scale degrees. A mode defines a transposablesubset of a tuning. Modes are discussed in a later section of this chapter.

tuning

A tuning is a scale with Hertz frequencies defined for each scale degree.Scale degrees may also have note names associated with them.tuning supports the following slot initializations:

:name {string | symbol}An optional name for the tuning.

:lowest numberThe lowest Hertz value defined in the scale. Defaults to 8.175, which is C-1 in the standard chromatic scale.

:cents listOne octave of the tuning specified as a list of cent values. Fewer or morethan 12 degrees may be specified and an “octave” does not have to

equal 1200 cents. If the cent values in list start with 0 and are inincreasing order they specify the cent difference from the lowest degree

to each degree above it. In this case the last value in list determines theoctave width of the scale. If list does not begin with 0 then the centsvalues describe the distances between adjacent steps. In this case the

Page 147: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

octave width is set to the sum of the cent steps. To define a tuning with

note names specify each step as a list: (cent {note}*) where cent is the

cent value followed by one or more note declarations. Each notedeclaration may consist of symbol note or a list (note &key

:accidental) where the value of :accidental is the portion of note thatrepresents the accidental. For example, the 8th step in the definition ofthe standard chromatic scale is:

(100 (af :accidental f) (gs :accidental s))

:ratios listExactly like cents except that the tuning values are specified as ratios(direct frequency scalers) rather than as cent values.

:octaves {number | list}The number of octaves defined in the scale. Defaults to 10. The value can

be a number or a list of two values (start end) where start is the first

octave number and end is the last octave number. Specify false if thetuning has no octaves.

:keynum-offset integerA integer offset to add to all keynums values returned by keynum. Defaultsto 0.

:default-octave integerThe default octave for notes without octave numbers. Defaults to the 4thoctave.

Tuning Systems

In the remaining sections of this chapter we present some examples of tuningsystems that demonstrate different approaches to temperament. There is reallyno way to adequately cover the full breadth and variety of different tuningsystems in a single chapter. The Web is an excellent resource for gatheringdescriptions of tuning systems; many of these descriptions provide ratios orcent specifications and can be easily implemented by tuning objects inCommon Music. Using Google to search for “Alternate Tuning” and “TuningSystems” will give you a good candidate list to start exploring!

Before presenting some example tunings we will first define a process toplay them. This process will “preview” the scale degrees of a specified tuning inascending, descending, and random order. This allows us to hear the tonalmaterial of the scale in several different contexts.

Example 15-5. A process to play tuning examples.

(define (play-tuning tuning low high rate amp) (let* ((rise (loop for i from low below high collect i)) (fall (loop for i downfrom high downto low collect i)) (ran1 (shuffle (rest rise))) (ran2 (shuffle (rest rise))) (coda (loop for i from low to high collect i)) (all (append rise fall ran1 ran2 coda))) (process for k in all for x = (keynum k :from tuning)

Page 148: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

output (new midi :time (now) :keynum x :amplitude amp :duration (* rate .95)) wait rate)))

The play-tuning process previews the scale degrees of tuning between a low

and high key number. The variables rise and fall contain the ascending and

descending patterns, while ran1 and ran2 hold randomized versions of thismaterial. Common Music's shuffle function randomly reorders lists of data,

and is discussed in Chapter 18. The coda segment of the melodic material

again ascends from low to high. All holds the entire list of key numbers to playand is created by appending all of the sublists together to form a single long list

of key numbers. The variable x holds a key number converted from the tuning toan equivalent value in the standard chromatic scale. The :from argument tokeynum converts an integer key number from the specified tuning object into anequivalent floating point key number in the standard chromatic scale.

The Harmonic Scale

We can treat any segment of the harmonic series as a scale by defining afunction that converts harmonic numbers into ratios. These ratios can then beused as the value of the :ratios initialization when the tuning is created(Example 15-6).

Example 15-6. Converting harmonic numbers to ratios.

(define (harms->ratios low high) (loop for h from low to high collect (/ h low)))

(new tuning :name 'jmaj :ratios (harms->ratios 8 16))

(new tuning :name 'jmin :ratios (harms->ratios 9 18))

In the first scale we use an eight note segment of the harmonic series from the8th to the 16th partial to give us a just major scale (Jmag), the second scale(Jmin) defines a nine tone octave based on the 9th partial with a just minor thirdbetween the 1st and 3rd scale degrees. Both tunings are based on a low C(8.125 Hz). Since there are eight tones per octave in Jmag and nine in Jmin,Middle C is key number 40 in Jmaj and key number 45 in Jmin.

Interaction 15-6. A process to play microtonal examples.

cm> (hertz 40 :in #&jmaj)261.6255653005986cm> (hertz 45 :in #&jmin)261.6255653005986

Page 149: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (keynum 40 :from #&jmaj)60.0cm> (keynum 41 :from #&jmaj)62.039100017307746cm> (events (play-tuning #&jmaj 40 56 .25 .6) "hscale.mid" :channel-tuning 9)"hscale-1.mid"cm> (events (play-tuning #&jmin 45 63 .25 .6) "hscale.mid" :channel-tuning 9)"hscale-2.mid"cm>

→ hscale-1.mid→ hscale-2.mid

Pythagorean Tuning

The Pythagorean Scale, named after the Greek philosopher Pythagoras ofSamos who discovered it, was the primary tuning system in Western music forabout 2000 years. Pythagoras noticed that a pleasing scale could be built usinga 3/2 ratio as the interval between each tone. Rather than work with steps thatare all 3/2 apart, the ratios in the Pythagorean scale are “normalized” to liewithin a single octave. This is accomplished by reducing any ratio greater than2 (the octave) by 1/2 such that the value again fits within a single octave. Forexample, the first tone in the scale (call it C) has a ratio of 1/1 = 1. The secondtone G is (1 * 3/2) = 3/2; the third tone D is (3/2 * 3/2 * 1/2) = 9/8; A is 9/8 * 3/2= 27/16 and so on. The tones are then ordered low to high to form anascending step wise pattern based on C. The missing F can be calculateddownward from C using 4/3, the interval complement of 3/2. Table 15-2 showsthe ratios of the seven-tone Pythagorean Diatonic Scale:

Table 15-2. Step ratios in the diatonic Pythagorean scale.

C D E F G A B C

1 9/8 81/64 4/3 3/2 27/16 243/128 2

Given our set of ratios we can create a tuning to implement the Pythagoreandiatonic scale:

Example 15-7. Tuning definition for the Pythagorean

Diatonic Scale

(new tuning :name 'pyth-d :ratios '(1 9/8 81/64 4/3 3/2 27/16 243/128 2))

The tuning named Pith-d in Example 15-7 defines seven tones per octavebased on a low C at 8.175 Hz. Since there are 7 notes per octave, Middle C iskey number 35 in pyth-d, and D4, which is key number 62 in the standardchromatic scale, is key number 36.

Page 150: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 15-7. Converting key numbers to and from atuning.

cm> (keynum 60 :to #&pyth-d)35cm> (keynum 70 :to #&pyth-d)35BUG!!!!!!!cm> (hertz 35 :in #&pyth-d)261.6255653005986cm> (keynum 35 :from #&pyth-d)60.0cm> (keynum 36 :from #&pyth-d)62.039100017307746cm>

We now listen to the sound of the Pythagorean diatonic scale. The key numbersspecified to play-tuning are in the coordinates of the pyth-d tuning system; theprocess then converts these to floating point equivalents in the standard scale.

Interaction 15-8. Playing the Pythagorean diatonic scale.

cm> (events (play-tuning #&pyth-d 35 49 .3 .7) "pyth.mid" :channel-tuning 9)"pyth-1.mid"cm>

→ pyth-1.mid

The Pythagorean Chromatic Scale

If we continue the 3/2 ratio series from Table 2 we can eventually generate all12 pitches in the cycle of 5ths (Table 3):

Table 15-3. Fifth ratios in the chromatic Pythagoreanscale.

F 3/2-1 2/3

C 3/20 1

G 3/21 3/2

D 3/22 9/4

A 3/23 27/8

E 3/24 81/16

B 3/25 243/32

F# 3/26 729/64

C# 3/27 2187/128

G# 3/28 6561/256

D# 3/29 19683/512

A# 3/210 59049/1024

E#/F 3/211 177147/2048

B#/C 3/212 531441/4096

The cycle of 5ths tells us that after a series of 12 5ths we should end up on apower of two, i.e. an octave doubling of our original frequency. In other words,

Page 151: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

for the cycle of 5ths to be a true circle, 3/212 must equal to 27. As we can plainlysee in Table 15-3 this is not true!

Interaction 15-9. The Big Lie.

cm> (expt 3/2 12)531441/4096cm> (expt 2 7)128cm> (= 531441/4096 128)#fcm> (/ 531441/4096 128.0)1.0136432647705078cm> (scaler->cents 1.0136432647705078)23cm>

The difference between 3/212 and 27 is 1.746337890625 or 23 cents. So the“cycle of fifths” is fiction, at least if one uses the 3/2 fifths that nature gives us.The 23 cent difference between the octaves and fifths is called the

Pythagorean comma and it is one of the central issues that designers faced asthey worked on the tuning systems that followed the Pythagorean Scale. Thediscrepancy between the cycle of fifths and octaves means that it ismathematically impossible to design a tuning system in which all intervals arejust while still remaining true to the octave.

There are other “problems” lurking in the Pythagorean tuning as well. Whilefifths and whole steps are just, both the major and minor thirds are out of tune.The Pythagorean major third is 81/64 (408 cents), 22 cents wider then the justmajor third, and the Pythagorean minor third 32/27 (294 cents) is 22 centssmaller than the just minor third. This 22 cent difference between Pythagoreanand just thirds is called the syntonic comma. Half steps in the Pythagoreanscale have a different problem. Notice that the ratio between E (81/64) and F(4/3) is (4/5*64/81=256/243, or 90 cents but the ratio between F and F-sharp is2187/2048, or 114 cents. This means that the chromatic Pythagorean scalehas two versions of the half step: 90 cents and 114 cents!

Interaction 15-10. Comparing major thirds, minor thirdsand minor seconds.

cm> (events (compare-ratios 'c4 5/4 81/64 3) "maj3.mid" :channel-tuning '(#t 1 3))"maj3-1.mid"cm> (events (compare-ratios 'c4 6/5 81/64 3) "min3.mid" :channel-tuning '(#t 1 3))"min3-1.mid"cm> (events (compare-ratios 'c4 256/243 2187/2048 3) "min2.mid" :channel-tuning '(#t 1 3))"min2-1.mid"cm>

Page 152: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

→ maj3-1.mid→ min3-1.mid→ min2-1.mid

We now define a version of the Pythagorean chromatic scale whose degreesare expressed by the cent measurements between its minor seconds:

Example 15-8. Pythagorean chromatic scale.

(new tuning :name 'pyth-c :cents '(114 90 90 114 90 114 90 114 90 90 114 90))

Interaction 15-11. Listening to the Pythagorean chromatic

scale.

cm> (events (play-tuning #&pyth-c 60 84 .3 .7) "pyth.mid" :channel-tuning 9)"pyth-2.mid"cm>

→ pyth-2.mid

Equal Temperament

In the Pythagorean scale fifths and whole steps are just but thirds are badly outof tune. As the role of the third became more important in Western music,composers searched for alternate tuning methods to replace the Pythagoreansystem. Different tunings arose, in part, because there are different ways todeal with the comma differences exhibited in the Pythagorean scale. Quarter

Comma Meantone Tuning distributes the syntonic comma equally over four5ths: C, G, D and A, which then allows the thirds within the fifths to remain just.But of course, each fifth at 696.5 cents is now out of tune, 1/4 syntonic comma(22/4 cents) flatter than the just fifth. Quarter Comma Meantone Tuning allowsclosely related keys to sound in tune while distant keys are badly out of tune.

Just Tuning tunes triads — a series of stacked major and minor triads arejustly tuned but more distantly related triads are out of tune. 12-tone equaltemperament takes a different approach altogether. Rather than keeping someratios just at the expense of others, equal temperament distributes the syntoniccomma equally over all 12 fifths, thereby reducing each just fifth by 2 cents to700 cents. This means that in equal temperament there are no just intervals atall, but all intervals are affected by only a very small amount. In Chapter 7 welearned that another way to think about equal temperament is as a system that

divides the octave into n equal half steps with a ratio size of 21/n between eachstep. In the case of the standard chromatic scale n is 12 and the step ratio is

21/12, or 1.0594630943592953. But of course there is nothing magical about12 steps, as we can see in Example 15-9 it is just as easy to design tuningsystems with more or fewer steps, or to base an equal temperament on an

Page 153: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

“octave” that is not a power of two.

Example 15-9. Alternate equal tempered scales.

(new tuning :name '5-tone :cents (loop repeat 5 collect (/ 1200 5)))

(new tuning :name '24-tone :cents (loop repeat 24 collect 50))

(new tuning :name 'bohlen-pierce :ratios (loop for i to 13 collect (expt 3.0 (/ i 13))))

The 5-tone equal tempered scale is a very rough approximation of theJavanese Slendro scale, which has no single exact tuning. 24-tone implementsquarter tone tuning. The Bohlen-Pierce scale is a 13-step scale with an octaveratio of 3/1 rather than 2/1. In other words, the “octave” for the Bohlen-Piercescale is the octave-plus-a-fifth interval between the first and third harmonics inthe harmonic series. There are also justly tuned chromatic and diatonicversions of the Bohlen-Pierce scale. For more information on this scale, consultCurrent Directions in Computer Music Research (Pierce) .

Pelog

Pelog is a 7-tone, non-octave scale characteristic of Javanese gamelan music.The degrees in the Pelog scale are named but there is no single relationshipdefined between them. Instead, each gamelan is tuned to its own version ofPelog. The two “octave” version shown in Example 15-10 was taken fromHarvard Dictionary of Music (Hdm) .

Example 15-10. A two octave version of the Pelog scale.

(new tuning :name 'pelog :lowest 220.0 :octaves #f :cents '((0 nem0) (125 barang0) (266 bem0) (563 gulu0) (676 dada0) (800 pelog0) (965 lima0) (1220 nem1) (1360 barang1) (1503 bem1) (1778 gulu1) (1905 dada1) (2021 pelog1) (2225 lima1) (2447 nem2)))

Page 154: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

La Monte Young's Well Tuned Piano

La Monte Young's piano composition The Well Tuned Piano is based on a 12-tone, justly tuned scale that features the “flat” 7th partial in its design. Acomplete description of the scale can be found in La Monte Young's The Well-

Tuned Piano (Gann) by Kyle Gann. Young's scale (Table 15-4) is a seven limittuning, which means that ratios defining the scale degrees contain no primenumbers greater than seven.

Table 15-4. Step ratios in Young's scale.

Eb E F F# G# G A

1/1 567/512 9/8 147/128 1323/1024 21/16 189/128

Bb B C# C D Eb

3/2 49/32 441/256 7/4 63/32 2

This tuning has a number of interesting features. First, it contains a wide varietyof interval sizes, ranging from the smallest, a 27 cents half-step between E andF, up to 204 cents, the just major second between E-flat and F. The scale'ssteps are also grouped into a pentatonic clustering around E-flat, F, G, B-flatand C in the standard chromatic scale. Another interesting feature of the scaleis its use of the “flat” seventh harmonic to establish its consonant thirdsrelations. Young's tuning uses a 9/7 major third between E and G-sharp, G andB, A and C-sharp, D and F-sharp, and a 7/6 minor third between F-sharp andA, B and D, and C-sharp and E. Another unusual feature is that its G-sharp islower than its G, and C-sharp is is lower than C! These pairs of tones werereordered to create 3/2 perfect fifths between the F-sharp, C-sharp and G-sharp spellings.

Example 15-11. Young's scale.

(new tuning :name 'young :ratios '((1 ef) (567/512 e) (9/8 f) (147/128 fs) (1323/1024 gs) (21/16 g) (189/128 a) (3/2 bf) (49/32 b) (441/256 cs) (7/4 c) (63/32 d) 2) :octaves '(-1 10) :lowest (* (hertz 'a-1) 128/189))

Page 155: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Young's scale is based on an E-flat that is actually closer to D in the standardchromatic scale. But the sixth scale degree, A, is tuned to exactly 440.0 Hz,which means that the scale's lowest E-flat can be calculated by multiplying thelowest A in the standard chromatic scale by 128/189, the reciprocal of the sixthdegree's ratio. The scale degree ratios in Example 15-11 are listed inmonotonically increasing order, which means that the symbols that define theG-sharp and C-sharp note names appear before their natural versions. The lastratio in the list is 2, which establishes the octave ratio without defining aredundant note name.

Interaction 15-12. Values in Young's scale.

cm> (note 0 :in #&young)ef-1cm> (hertz 'a4 :in #&young)440.0cm> (keynum 'c4 :in #&young)70cm> (hertz 'c4 :in #&young)521.4814814814814cm>

The following musical examples, implemented by Michael Klingbeil,demonstrate Young's tuning.

Example 15-12. A process to preview Young's scale.

(define (arp-chord notes rate dur amp) ;; arpeggiate a list of notes. (process with transp = 0 for n in notes for k = (keynum n :from #&young) output (new midi :time (now) :keynum (+ transp k) :duration dur :amplitude amp) wait rate))

The following two examples demonstrate the basic scaler and intervalicrelations in Young's scale.

Example 15-13. Scale and septimal interval relations.

(define youngs-scale '(ef4 e4 f4 fs4 gs4 g4 a4 bf4 b4 cs5 c5 d5 ef5))(define sept-7th ; 7/4 seventh '(ef4 c5))(define sept-min-3rds ; 7/6 thirds '(e4 gs4 f4 g4 a4 cs5))(define sept-maj-3rds ; 9/7 thirds '(fs4 a4 b4 d5 cs5 e5 c5 f5))

Page 156: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 15-13. Listening to the scale and intervals.

cm> (events (arp-chord youngs-scale 0.4 2 .6) "young.mid" :channel-tuning true)"young-1.mid"cm> (events (play-tuning #&young 48 96 .15 .5) "young.mid")"young-2.mid"cm> (events (arp-chord sept-7th 0 3 .6) "young.mid")"young-3.mid"cm> (events (arp-chord sept-min-3rds 0 2 .6) "young.mid")"young-4.mid"cm> (events (arp-chord sept-maj-3rds 0 2 .6) "young.mid")"young-5.mid"cm>

→ young-1.mid→ young-2.mid→ young-3.mid→ young-4.mid→ young-5.mid

Example 15-14 defines some representative sonorities that Young uses in TheWell Tuned Piano.

Example 15-14. Chords from The Well Tuned Piano.

(define opening-chord '(ef3 bf3 c4 ef4 f4 bf4))(define magic-chord '(e3 fs3 a3 b3 d4 e4 g4 a4))(define gamelan-chord '(fs3 a3 c4 e4))(define tamiar-dream-chord '(b2 d3 g3 a3 b3 d4 g4 a4))(define lost-ancestral-lake-region-chord '(g2 b2 d3 fs3 g3 a3))(define brook-chord '(bf3 c4 ef4 f4 g4 bf4 c5 ef5 f5 bf5))(define pool-chord '(ef3 f3 fs3 bf3 c4 ef4 f4 bf4))

Interaction 15-14 demonstrates the Opening Chord, Magic Chord, GamelanChord, Tamiar Dream Chord, 89/89 Lost Ancestral Lake Region, The Brook,and The Pool chords:

Interaction 15-14. Sonorities from The Well Tempered

Piano.

Page 157: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Class]

cm> (events (arp-chord opening-chord 0.25 3 .6) "young.mid" :channel-tuning true)"young-6.mid"cm> (events (arp-chord magic-chord 0.25 3 .6) "young.mid")"young-7.mid"cm> (events (arp-chord gamelan-chord 0.25 3 .6) "young.mid")"young-8.mid"cm> (events (arp-chord tamiar-dream-chord 0.25 3 .6) "young.mid")"young-9.mid"cm> (events (arp-chord lost-ancestral-lake-region-chord 0.25 3 .6) "young.mid")"young-10.mid"cm> (events (arp-chord brook-chord .15 5 .6) "young.mid")"young-11.mid"cm> (events (arp-chord pool-chord .15 5 .6) "young.mid")"young-12.mid"cm>

→ young-6.mid→ young-7.mid→ young-8.mid→ young-9.mid→ young-10.mid→ young-11.mid→ young-12.mid

Modes

A mode in Common Music is a scale that defines a transposable subset of atuning. Adjacent degrees in the mode may be non-contiguous in the tuning.

mode

mode supports the following slot initializations:

:name {string | symbol}An optional name for the mode.

:tonic noteSets the tonic note name of the mode. Defaults to the first note in thesteps specification, or C if the steps are described as intervals.

:scale tuningSets the tuning system for the scale. Notes in the mode must also benotes in the tuning. Defaults to the standard chromatic scale.

:steps {notes | intervals}One octave of the mode specified as a list of notes or list of intervalsbetween notes. If steps is a list of notes the tonic (transposition offset) ofthe mode is automatically set to the note class of the first note in the list. If

Page 158: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

the steps are specified as a list of intervals the transposition is set to C. Amode can be transposed to a new tonic using the transpose function.

Functions like hertz, keynum, note and transpose accept modes as well astunings as scale inputs:

Interaction 15-15. Transposing modes.

cm> (new mode :name 'dorian :steps '(d e f g a b c d))1cm> (keynum 'd4 :in #&dorian)35cm> (loop for i from 35 to 42 collect (note i :in #&dorian))(d4 e4 f4 g4 a4 b4 c5 d5)cm> (transpose #&dorian 'cs)2cm> (loop for i from 35 repeat 45 collect (note i :in #&dorian))(cs4 d4 e4 es4 fss4 gs4 as4 b4 cs5)cm>

To demonstrate the use of modes we define a process that harmonizesrandom chords on Messiaen's Modes of Limited Transposition.

Example 15-15. Messiaen's Modes of LimitedTransposition.

(new mode :name 'mode1 :steps '(c d e fs gs bf c))(new mode :name 'mode2 :steps '(c df ef e fs g a bf c))(new mode :name 'mode3 :steps '(c d ef e fs g af bf b c))(new mode :name 'mode4 :steps '(c df d f fs g af b c))(new mode :name 'mode5 :steps '(c df f fs g b c))(new mode :name 'mode6 :steps '(c d ef fs gs as b c))(new mode :name 'mode7 :steps '(c df d ef f fs g af a b c))

(define chords '((0 2 4 5) (0 1 3 5) (0 2 3 4)))

(define (messiaen mode start end rate) (let ((num (length chords))) (process for m from start to end for c = (list-ref chords (random num)) for l = (note (transpose c m) :in mode) each n in l output (new midi :time (now) :keynum n :duration (* rate 2)) wait (odds .15 (* rate .5) rate))))

The messiaen process selects chords at random from a list of possible chordtemplates defined in the global variable chords. Each chord template is a list ofintervals that will be transposed to specific key numbers in the mode specifiedto the process. The wait expression uses Common Music's odds function to

Page 159: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

randomly produce “added value” rhythms that are characteristic of much ofMessiaen's music.

Interaction 15-16. Listening to Messiaen's Modes

cm> (events (messiaen #&mode2 35 50 .4) "messiaen.mid")"messiaen-1.mid"cm> (events (messiaen #&mode6 35 50 .4) "messiaen.mid")"messiaen-2.mid"cm>

→ messiaen-1.mid→ messiaen-2.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file scales.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

References

Mathews, M. & Pierce, J. (1989). The Bohlen-Pierce Scale. In Mathews V., &Pierce, J. (Eds.), Current Direction in Computer Music Research (pp.165-174).Cambridge: The MIT Press.NIL (1972). Harvard Dictionary of Music. In Apel, W. (Eds.), NIL (pp.NIL).Boston: Harvard University Press.Gann, K. (1993). La Monte Young's The Well-Tuned Piano. Perspectives ofNew Music, 31(1), 134-162.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 160: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

16Mapping

A mapping is between two things: it is thelife of the argument, linking and identifyingand relating. The life of music may havesome relationship to this concept as well:

how else would music be able to teachitself, unless there were a process at work

that made connections, showedsimilarities, identified differences, andconveyed their significance?

— Edward Rothstein, Emblems Of Mind:The Inner Life of Music and Mathematics

Proportion, juxtaposition and balance between musical ideas are fundamentalrelationships in music, and transformations that affect these propertiespermeate the craft of composition. Scaling and offsetting are two simple yetpowerful ideas that composers use to transform musical ideas. In this chapterwe examine some computer techniques related to scaling and offsetting anddemonstrate how they can be used to control the procedural description ofparameterized sound data.

Scaling and Offsetting

As its name suggests, scaling a quantity means to change its size, ormagnitude, by multiplication. In the visual domain, scaling causes an object toappear bigger or smaller without altering its origin with respect to the visual field.In music composition, the idea of scaling can be observed in our system ofmensural rhythmic proportions, augmentation and diminution, in the harmonicseries and tuning systems, and in amplitude shapes such as the crescendo anddecrescendo. Offsetting a quantity means to add a value to it. Offsetting affectsthe origin of something along an axis of interest. Offsetting can be observed inmusic in semitonal transposition, used extensively in the 12-tone system.Rhythmic effects such as syncopation and the added values of Messiaen arealso “offset effects”, as is the sequencing of motives and intervallic patterns.

Mapping

Page 161: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Throughout history composers have been interested in projecting extra-musicalideas into sound. Dufay's famous isorhythmic motet Nuper Rosarum Flores wascomposed using the architectural ratios of the dome of the Santa Maria delFiore cathedral in Florence. These proportions were transformed into musicalproportions for his motet commissioned for the cathedral's opening in 1436.See Charles Warren's Brunelleschi's Dome and Dufay's motet (Warren) , MusicQuarterly. In the twentieth-century, Charles Dodge's composition The Earth'sMagnetic Field is based on measurements of the Earth's yearly averagemagnetic activity that are projected onto a set of pitches for the eight minutepiece. The general term for this type of transformation is called mapping.Mapping means to relate, or project, values from one domain of interest intoanother. We will implement a simple mapping function called rescale that usesonly scaling and offsetting to transform values from one coordinate system intoanother.

The Rescale Function

The rescale function implements a simple, general tool for effectingtransformations by scaling and offsetting. The simplicity of the function belies itsexpressive power — we will have opportunities to use this function many timesthroughout the remaineder of this book to reformulate values from one range intoanother, or to translate a value in one parameter into a value for anotherparameter, or to project non-musical data into musical parameter space. Therescale function is defined by Common Music but is presented here in Example16-1 to show how scaling and offsetting can be implemented.

Example 16-1. Common Music's rescale function.

(define (rescale x old-min old-max new-min new-max) ;; scale the size of x by the proportion between ranges ;; and then shift into the new coordinate system. (let ((old-range (- old-max old-min)) (new-range (- new-max new-min)) (old-size (- x old-min))) (+ (* (/ new-range old-range) old-size) new-min)))

The rescale function takes a value x that lies within the inclusive boundaries old-

min to old-max and returns a value equivalent to x that now lies between theboundaries new-min to new-max. The values x, old-min and old-max are first

converted to proportional quantities by subtracting out the old-min offset. Thelocal variable old-range is the difference between old-max and old-min, while

new-range is the same but between new-max to new-min. The body of thefunction is a single nested expression containing division, multiplication andaddition terms. Division and multiplication are inverse scaling operations andaddition is offsetting. The expression (/ old-range new-range>) is the ratio ofthe magnitudes of the two ranges. This ratio scales old-size, the magnitude of

the value x. If new-range is larger than old-range then the multiplication will

increase old-size. If new-range is smaller than old-range then old-size will

Page 162: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

decrease by the same proportion. The outermost addition in the expressionoffsets the rescaled old-size by new-min, the minimum value of the newcoordinate system, which shifts the new quantity back within the y boundariesspecified to the function. A few sample interactions will suffice to show how thefunction operates:

Interaction 16-1. Examples of rescale.

cm> (rescale 5 0 10 10 20)15cm> (rescale 5 0 10 0 40)20cm> (rescale 5 0 10 100 200)150cm> (rescale 5 0 10 -200 0)-100cm> (rescale 2 0 10 200 100)180cm>

The first two interactions demonstrate offsetting and scaling in isolation. The firstexpression shifts, or offsets, the value 5, which lies half way between oldboundaries 0 and 10, to the value 15, which lies half-way between the newvalues 10 and 20. The second interaction scales the value 5 to a value four timesas large. The remaining three interactions combine scaling and offsetting. Thethird interaction rescales 5 to lie halfway between the boundaries 100 and 200.The fourth interaction transforms 5 to lie within negative boundaries. The lastinteraction inverts 5 within the new boundaries by reversing the new-max and

new-min values.

Points on a Curve To Find?

We now use the rescale function to define a musical process that maps valuesof the sin function, which range from -1 to 1, onto a range of MIDI key numbers.

Example 16-2. Playing MIDI notes along a sine curve.

(define (play-sine len cycs key1 key2 rhy dur amp) (let ((maxx (- len 1)) (maxr (* 2 pi cycs))) (process for x from 0 to maxx ;; rescale i to radians for r = (rescale x 0 maxx 0 maxr) ;; calc sine value -1 to 1 of r for a = (sin r) ;; rescale sin to keynum for k = (rescale a -1 1 key1 key2) output (new midi :time (now) :keynum k :amplitude amp :duration dur) wait rhy)))

Page 163: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

play-sine plays MIDI events as discrete points along cycs cycles of a“sinewave”. We learned in Chapter 10 that a sinewave describes a smoothoscillation between a minimum and a maximum amplitude over time.Instantaneous points along this smoothly changing shape can be calculatedusing the trigonometric function sin. The sin function computes the ratio of thehypotenuse to the opposite side of an angle as it sweeps around the unit circle

(Figure 1.) The angular input to sin is expressed in term of radians, not degrees.Radians are just another way to measure angles, and there are 2pi radians in360º.

Figure 16-1. The sine function.

As the angle sweeps from 0 to 2pi radians (0-360º) the value of sin producesone oscillation between -1 and 1. At 0 radians sin is 0, at pi/2 radians (90º) it is1, at pi radians (180º) it is 0 again, and at 3pi/2 radians (270º) it is -1. The cyclerepeats itself every 2pi radians (360º), which is same as 0.

Play-sine uses rescale to map a counter i onto an equivalent radian value rand to map values of sin onto key numbers. Len is the total number of events

that play-sine creates, and cycs is the number of cycles of the “sine wave”

produced over len events. Since there are 2*pi radians in one cycle of a sine

wave, the maximum radian value maxr is 2*pi*cycs radians over cycs cycles of

the sine wave. Each time the process runs, r is set to the current radian valuedetermined by rescaling the x counter into radians. The current sine amplitude

value a is then calculated and converted to the current key number k using therescale function again.

Interaction 16-2. Listening to play-sine.

cm> (events (play-sine 80 4 20 100 .1 .1 .6) "sine.mid")"sine-1.mid"cm> (events (list (play-sine 80 4 20 100 .1 .1 .6) (play-sine 60 5.7 50 80 .1 .1 .6)) "sine.mid" '(0 2))"sine-2.mid"cm>

→ sine-1.mid→ sine-2.mid

Page 164: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

In the next example we define a variation of the play-sine process thatcalculates MIDI notes along the sine curve based on the current time of theprocess. This version allows us to listen to different densities of events along thecurve. We will also use the rescale function to control amplitude using the samesin values that control key numbers.

Example 16-3. Variation of sine wave process.

(define (play-sine2 end cycs rate keymin keymax ampmin ampmax) (process for r = (rescale (now) 0 end 0 (* 2 pi cycs)) for a = (sin r) while (< (now) end) output (new midi :time (now) :keynum (rescale a -1 1 keymin keymax) ;; invert sine shape for amplitude :amplitude (rescale a -1 1 ampmax ampmin)) wait rate))

End is the stopping time for the algorithm and rate is the speed between MIDIevents in seconds. Cycs is the total number of cycles to produce: as cycs

increases for a given rate there will be fewer and fewer events per cycle of thesine curve.

Interaction 16-3. Listening to play-sine2.

cm> (events (play-sine2 10 .1 6 20 70 .1 .9) "sine.mid")"sine-3.mid"cm> (events (play-sine2 10 .5 6 20 70 .1 .9) "sine.mid")"sine-4.mid"cm>

→ sine-3.mid→ sine-4.mid

Linear Interpolation

The mathematical operation that rescale performs is called linear interpolation.

The x on which the equivalence is based is called the “independent”variable and

the output value that rescale calculates is called the “dependent”, or y, variable.

We can use a loop to collect pairs of x and y interpolation values into a list:

Interaction 16-4. X and y interpolation pairs.

cm> (loop for x in '(0 .2 .4 .6 .8 1) for y = (rescale x 0 1 100 200) collect x collect y)(0 100 0.2 120.0 0.4 140.0 0.6 160.0 0.8 180.0 1 200)cm> (loop for x in '(0 .25 .5 .75 1) for y = (rescale x 0 1 440 220) collect x collect y)(0 440 0.25 385.0 0.5 330.0 0.75 275.0 1 220)

Page 165: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm>

The linear relationship between these values can be shown by graphing the x

and y pairs. The independent x value ranges along the horizontal, or x axis, andthe dependent y values range over the vertical, or y axis (Figure 16-2).

Figure 16-2. Graphic display of x and y pairs in Example 2.

The points in the plot mark the positions of values along both axes. We can seein both graphs that linear interpolation produces values on a straight linebetween a minimum and maximum value and that the slope of the line may beupward or downward.

Envelopes

The list of x and y pairs in Interaction 16-4 form a data structure called an

envelope. Each pair of values defines a single coordinate or point, in the

envelope. The x values in the envelope must be specified in monotonically

increasing order to insure that only one unique y value can exist for every uniquex value. The y values in the envelope are not ordered, they may range anywherewithin the minimum and maximum y axis values (Interaction 16-5 and Figure 16-3).

Interaction 16-5. x and y interpolation pairs.

cm> (loop for x in '(0 .2 .4 .6 .8 1) for y = (between 40 80) collect x collect y)(0 50 20 83 40 59 60 66 80 51 100 66)cm>

Figure 16-3. Graphic display of x and y pairs in Example 2.

Page 166: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

Common Music provides two functions that perform interpolation with envelopes:

(interp x x1 y1 x2 y2 ...)

(interpl x env [:scale s] [:offset o] [:base b])

Both functions perform linear interpolation over a series of point specifications.

The first argument to both functions is the independent x value to interpolate. The

other arguments to interp are the sequences of x coordinate pairs that definethe shape of the envelope. In contrast, the interpl function takes an envelopelist as its second argument. The two calls shown in Interaction 16-6 areequivalent.

Interaction 16-6. interp and interpl

cm> (interp .5 0 0 1 100)50cm> (interpl .5 '(0 0 1 100))50cm>

The interpl function also supports a set of optional keyword arguments afterthe envelope specification. Recall from Chapter 3 that a keyword argument isexpressed as a pair: the keyword name of the argument followed by its value(Interaction 16-7).

Interaction 16-7. Passing keyword arguments to the interpl

function.

cm> (interpl 0.5 '(0 0 1 1) :scale 100)50.0cm> (interpl 0.5 '(0 0 1 1) :offset 100)100.5cm> (interpl 0.5 '(0 0 1 1) :scale 100 :offset 100)150cm>

The next example demonstrates how interp can be used to control parametervalues in a musical process. The process interpolates amplitude and tempovalues using two interp expressions. Each interpolation divides the total length

Page 167: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

of the process into two segments on either side of a randomly chosen midpoint

mid between 20 and 80 percent of the total length of the process. Eachinterpolation produces increasing values over the first segment and decreasingvalues over the second.

Example 16-4. Interpolating amplitude values from event

number.

(define (play-interp len rate key1 key2 mintem maxtem minamp maxamp) (let* ((end (- len 1)) (mid (+ (* .2 len) (random (* .8 len))))) (process for x from 0 to end for a = (interp x 0 minamp mid maxamp end minamp) for c = (interp x 0 mintem mid maxtem end mintem) output (new midi :time (now) :keynum (+ key1 (random key2)) :amplitude a :duration (* rate 1.5 c)) wait (* rate c))))

In Interaction 16-8 we use a loop to sprout six versions of the process, each witha start time .5 seconds later than the preceding one.

Interaction 16-8. Listening to play-interpl

cm> (events (loop for k in (keynum '(c6 f5 bf4 ef4 af3 df3)) for s from 0 by .5 collect (play-interp 40 .2 k (+ k 7) 1 .6 .4 .8)) "interp.mid")"interp-1.mid"cm>

→ interp-1.mid

Normalized Envelopes

The envelope is a very simple yet powerful data structure in computercomposition because the “shape” it defines can be scaled and offset in controlthe evolution of one or more sound parameters (Example 16-5 and Figure 16-4).

A normalized envelope is an envelope whose xy coordinates are defined withinthe range 0 to 1. Normalized envelopes express proportion, or shape, ratherthan absolute values. Normalized envelope shapes are generally useful toolsand composers often define global variables to hold the most commonenvelopes they work with. Example 16-5 shows some sample normalizedenvelope definitions and their shapes are shown in Figure 16-4.

Example 16-5. Common envelope shapes.

(define ramp-up '(0 0 1 1))

Page 168: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define ramp-down '(1 0 0 1))(define tri-env '(0 0 .5 1 1 0))(define mid-env '(0 0 .25 1 .75 1 1 0))(define exp-down '(0 1 .25 1/4 .5 1/16 .75 1/32 1 0))(define exp-up '(0 0 .25 1/32 .5 1/16 .75 1/4 1 1))

Figure 16-4. Plots of envelope shapes.

To use a normalized envelope the y output value must be rescaled and offset tolies within the appropriate range for a given parameter. The interpl functioncan perform both of these actions in a single function call (Example 16-6 andInteraction 16-9).

Example 16-6. Applying normalized shapes to a process.

(define (shaper len env rate ampscale ampoff keyscale keyoff) (let ((last (- len 1))) (process for i below len for x = (/ i last) for a = (interpl x env :scale ampscale :offset ampoff) for k = (interpl x env :scale keyscale :offset keyoff) output (new midi :time (now) :duration rate :keynum k :amplitude a) wait rate)))

Interaction 16-9. Calling shaper with different envelopes.

cm> (events (shaper 20 tri-env .15 .7 .05 16 48) "shaper.mid")"shaper-1.mid"cm> (events (shaper 20 exp-down .15 .5 .2 20 30) "shaper.mid")"shaper-2.mid"cm>

→ shaper-1.mid→ shaper-2.mid

Page 169: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

Exponential Scaling

Linear interpolation is a wonderful tool, it provides a simple and general way tocontrol sound parameters as a function of some variable whose domain may notbe directly related to the parameter in question. However, there are times whenthe “straight line” effect produced by linear interpolation may be inappropriate.For example, changes in tempo and amplitude often sound more natural if they

are produced by exponential scaling rather than linear interpolation. Valuesgenerated by exponential scaling lie on a power curve rather than on a straightline. The :base keyword argument to interpl can be used to produceinterpolation along a power curve. If :base is 1 (the default) then linearinterpolation results. If :base is greater than 1 the interpolated values lie on aconvex curve for positive slopes, or concave curves if it is less than 1 (Figure 16-5.) The more :base deviates from 1 the steeper the power curve between points.

Figure 16-5. Exponential interpolation using the envelope

in Figure 3.

Common Music provides several additional functions for working withexponential scaling.

(expl x [:y0 n] [:y1 num] [:base num])

(explsegs len sum base)

The expl function returns an exponential value between :y0 and :y1 for the given

x value that must range from 0 to 1. Explsegs returns a list of len number of

exponentially scaled segments that add up to sum. The :base arguments tothese functions are the same as for interpl. explsegs allows the composer toset the total size for some quantity and then chop it up into a number ofexponentially related segments. The next example defines a process calledballfall whose tempo and key numbers are controlled by a list of exponentialvalues produced by explsegs.

Example 16-7. Exponential curve for falling ball.

(define (ballfall drops end curve key amp) (let* ((all (explsegs drops end curve)) (max (first all)) (low (hertz key)))

Page 170: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(process for d in all for e = (random (expt 2.0 (/ d max))) for k = (keynum (* low e) :hz) for a = (rescale d 0 max .1 amp) output (new midi time (now) :duration d :keynum k :amplitude a) wait d)))

Drops are the number of times the ball bounces and end is the time it takes to

decay. Curve controls the steepness of the power curve. Since the longest dropoccurs at the beginning the curve value should always be less than 1 for thisprocess. Key is the lowest key number the ball will play and amp is the

maximum amplitude. The variable all is set to the list of exponential values

returned by explsegs. Since curve is less than 1 the first value in this list is the

maximum drop value max. The process iterates for each drop value d in all. On

each iteration, the variable e is set to a random value between 1 and 2 based on

the proportion of the current drop d to the longest drop max. This means that as

the process iterates, the range for successive random values of e diminishesfrom 2.0 to a value approaching 1.0 (Figure 16-6.) This value is then used to

transpose the low frequency to somewhere within an octave and k is set to thekey number of that Hertz frequency.

Figure 16-6. X axis plots start time, y axis plots range (redlines) and value of random key number (for drop=25,

end=20, curve=1/20).

The amplitude variable a is determined by rescaling the current drop value to avalue between .1 and the maximum amplitude specified to the process.

Interaction 16-10. Exponential curve for tempo and key

number.

cm> (events (ballfall 40 10 1/256 60 .8) "ball.mid")"ball-1.mid"cm> (events (ballfall 100 15 1/150 40 .8) "ball.mid")"ball-2.mid"cm>

→ ball-1.mid

Page 171: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

→ ball-2.mid

Self-Similar Scaling and Recursion

Scaling and offsetting can be combined to produce patterns that consist entirelyof a single shape that is projected in identical proportions at smaller and smaller

magnitudes. This kind of pattern is called a self-similar, or fractal pattern. Self-similarity can be found not just in visual patterns, but in musical ones as well. Forexample, the intervals in the chromatic scale are derived from a single ratio of

21/12. An equal-tempered melody sounds the same when transposed to differentoctaves even though the actual Hertz frequency values between the tones are notequivalent.

One simple way to create a self-similar pattern is to apply the same set ofrules (i.e. an algorithm) over and over again at different levels of magnitude untilthe pattern has been generated to a sufficient resolution. Self-similarity is mostnaturally implemented using a recursive function definition. A recursive functionis a function that calls itself to solve successively simplified versions of aproblem. In order to illustrate self-similarity and recursion we will create afunction that simply “chops up” a quantity into self-similar segments at smallerand smaller magnitudes and then returns a list of all the segments that itcalculates. For example if an input of 8 were chopped into 2 segments over 4levels of magnitude the algorithm would return the list:

(8 4 2 1 1 2 1 1 4 2 1 1 2 1 1)

which corresponds to the levels and proportions in Table 16-1.

Table 16-1. Levels and proportions of a self-similar

organization.

Level 1 8

Level 2 4 4

Level 3 2 2 2 2

Level 4 1 1 1 1 1 1 1 1

The sschop function Example 16-8 is a recursive algorithm that calls itself toreproduce the exact same behavior at different levels of magnitude. Its recursivenature means that sschop reflects self-similarity in its definition as well as in theresults it returns.

Example 16-8. Self-similar segments of a line.

(define (sschop value segments levels) (if (< levels 1) '() (let* ((nextval (/ value segments)) (nextlev (- levels 1))

Page 172: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(chopped (loop repeat segments append (sschop nextval segments nextlev)))) (list* value chopped))))

The function sschop takes three inputs: a value to chop, the number of segmentsto chop value into, and the number of magnitude levels to chop over. Thefunction returns a list of chopped values. The body of sschop consists of a singleif expression. If the value of levels is less than 1 then sschop returns an empty

list (Interaction 16-11). If levels is greater than zero then sschop calculates a listof values to return. These values consist of the current value added to the front ofa list containing successive levels of chopped values at proportionally smaller

sizes. The let* establishes three variables. nextval is the proportionally smallernext value, computed by dividing the current value by the number of segments in

each level. nextlev is set to level-1. Chopped holds the list of segments thatconstitute all the values under nextlev. This value is computed by a loop that

iterates segments number of times and appends the results of a recursive call tosschop at the next smaller size and with levels reduced by one. Since sschopalways returns a list, the append clause is used to concatenate each sublist intoone large list. The list* function then adds value to the front of this list so thatthe current level's value is in front of all of its recursively generated segments.Interaction 16-11 shows several sample calls to sschop that demonstrate its self-similar nature.

Interaction 16-11. Calling sschop.

cm> (sschop 440 2 0)()cm> (sschop 440 2 3)(440 220 110 110 220 110 110)cm> (sschop 36 3 3)(36 12 4 4 4 12 4 4 4 12 4 4 4)cm>

Sierpinski's Triangle

Sierpinksi's Triangle is a famous two-dimensional fractal (Figure 16-7). Therules to create Sierpinski's triangle are simple:

1. Start with a triangle.2. Draw a smaller triangle in the middle of the larger triangle with the points of

the smaller triangle on the midpoints of the larger triangle's sides. Thispartitions the large triangle into 4 smaller triangles: a middle triangle andthe triangles at each of the corners of the larger triangle.

3. Leave the middle triangle empty.4. Visit each of the corner triangles in the larger triangle and repeat the rules

starting with step 1 but now using each of these smaller triangles as thestarting triangle.

5. Repeat for as many levels as you want: the result is Sierpinski's Triangle.

Page 173: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Figure 16-7. A four-level Sierpinsk Triangle.

We will now transform our sschop algorithm into a musical process thatgenerates a self-similar melody as an “homage” to Sierpinski's Triangle. Ouralgorithm will use the basic method described in sshop to generate its self-similar melody from of set of three tones, where each tone represents the side ofa triangle.

Example 16-9. Sierpinski's Melody

(define (sierpinski tone melody levels dur amp ) (let ((len (length melody))) (process for i in melody for k = (transpose tone i) ;; play current tone in melody output (new midi :time (now) :duration dur :amplitude amp :keynum k :channel 0) when (> levels 1) ;; sprout melody on tone at next level sprout (sierpinski (transpose k 12) melody (- levels 1) (/ dur len) amp) wait dur)))

The sierpinski process reproduces a melodic shape based on successivedegrees and octaves of itself. Tone is the current melodic tone to play, melody

is a list of intervals representing the melody, level is the number of levels of themelody to produce, and dur and amp are the tone's duration and amplitude,

respectively. The variable len is set to the number of intervals in the melody. Theprocess first outputs the current tone If levels is greater then 1 then the processsprouts a recursive process definition to generate the next melodic level. Thenext level's tone is calculated by transposing the current tone in the melody upone octave. The duration for each tone becomes the current level's durationdivided by the number of intervals in the melody. Thus, the entire melody in thenext level will occupy the same amount of time as the current tone in the current

Page 174: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

level (Figure 16-8).

Figure 16-8. Self-similar melody from sierpinski.

The value for levels is decremented by 1 , which will cause the recursivegeneration to finally stop when this value reaches 0.

Interaction 16-12. Listening to sierpinski.

cm> (events (sierpinski 'a0 '(0 7 5 ) 4 3 .5) "sier.mid")"sier-1.mid"cm> (events (sierpinski 'a0 '(0 -1 2 13) 5 24 .5) "sier.mid")"sier-2.mid"cm>

→ sier-1.mid→ sier-2.mid

Specify levels and melody with care! The number of events the sierpinskiprocess generates is exponentially related to the length m of the melody and the

number of levels l:

Accordingly, the first events call in Interaction 16-12 generates 120 events andthe second produces 1364.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file mapping.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

References

Page 176: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

17Etudes, Op. 6: Sonification ofChaos

Thru all the tumult and the strife,I hear that music ringing.

It sounds and echoes in my soul;

How can I keep from singing?

— Quaker, Worship Comes Alive

Sonification means to use an acoustic signal to make information inherent in acollection of data audible (see the on-line report Sonification Report: Status ofthe Field and Research Agenda (Kramer) by Gregory Kramer for an overviewof research in this area.) Sonification is to the ear what graphic plotting is to theeye — a way to understand relationships that would otherwise be difficult orimpossible to perceive. Our ears are very keen instruments that can detectsubtle, multidimensional changes in sound space. We have probably all had theexperience of diagnosing a problem with a car by listening to minute variationsin the sound it produces in order to locate the source of the mechanicalproblem. Sonification can yield information about regularity, overall shape,grouping, transitions, and deviations in the data that is presented in an acoustic

signal. Parameter mapping is one of the most common ways to implementsonification. By projecting data onto frequency, amplitude and timbre over time,many dimensions of data can be perceived and related simultaneously. In thisEtude we apply the mapping techniques presented in Chapter 16 to data setsgenerated by several chaotic functions. The text and examples in this Etudewere written by Tobias Kunze for a workshop on algorithmic composition heldat CCRMA, Stanford University. Our aims in the Etude are twofold: tounderstand how mapping can be implemented given a set of non-musicalequations, and to demonstrate how sonification can reveal the behavior of non-

musical functions. The term deterministic chaos refers to the behavior ofsystems that are not random but are nevertheless unpredictable in nature. Thephysical world is filled with complex dynamic processes (like turbulence andweather) that are deterministic but whose behavior is nevertheless impossibleto predict. There are several features that characterize these systems. First,chaotic systems are highly sensitive to initial conditions — in one startingarrangement their elements produces periodicity; in another, closely proximatearrangement, complete unpredictability ensues. This is because chaotic

systems are nonlinear, which means that changes in one element do not

Page 177: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

produce a like, or equivalent change in others. This means the cause and effectrelationships are not proportional in a chaotic system, so that two functions for asingle equation may evolve in a similar way but then suddenly diverge along

very different paths (Figure 17-3). The butterfly effect observed first by themeteorologist Edward Lorenz encapsulates the sensitivity and nonlinear natureof these systems quite well: a butterfly flaps its wings in Tokyo and it rains inNew York.

Op. 6 No. 1, The Logistic Map

The logistic map is a simple example of a discrete dynamical system thatactually names a whole family of iterative functions described by the LogisticEquation:

fn+1 = cfn(1-fn); for 0.0 ≤ c ≤ 4.0

Each real value of c in the equation yields a different dynamic behavior andhence a different function.

Background

The logistic map models population growth in a basic activator-inhibitorsystem. Although population growth rates are constant in an ideal environment,actual growth rates are not due to limited resources such as food and waterand natural catastrophe. If resources are limited, the growth rate slows down as

a population increases and eventually reaches a limit or attractor at which pointthe population is at its maximum capacity for the environment. The S-shaped

curve of population growth in such an environment is known as the logisticcurve (Figure 17-1).

Figure 17-1. Expected growth curve.

Resource limitations may be viewed as “passive” inhibitors, that is, they onlyslow down population growth. However, most environments also involve “active”inhibitors, such as floods and epidemics, which reduce a population or at timescause extinction. Whether populations in such an environment approach amaximum, die out or tend to converge towards a number in between theseextremes is hard to predict, since most systems with active inhibitors behave in

a chaotic manner. Each function of the logistic map may serve as an algebraic

Page 178: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

model of such a chaotic environment.

Activation and Inhibition

As a model for the development of populations (and similar processes), thelogistic map may be broken down into activating and inhibiting terms, relatedby a function R with a weighting constant c applied to one of the terms: f=(c *

activator)RInhibitor. R happens to be the multiplication operator in the logisticmap (Figure 17-2) although other possibilities are imaginable.

Figure 17-2. Activator term x (rise) and inhibitor term 1-x(fall) of the logistic map and their product (curve), which

exhibits a global maximum of 0.25. To keep the finalproduct c*x(1-x) stable between 0.0 and 1.0 it is required

that 0≤c≤4.0.

Chaos and The Butterfly Effect

In contrast to the many random processes which produce indeterministicbehavior, the logistic map is strictly deterministic. For instance, we can iterate a

given function of the logistic map n times to determine its nth value. The nth

value of the function is determined solely by the initial value, f0and the function

constant c. But exactly how the nth value is determined by the initial conditionsis hard to predict. Figure 17-3 displays two processes with only minimallyaltered initial states. But after an initial phase of almost parallel evolution thisdifference leads to totally different behavior between the processes. Moreover,it is almost impossible to predict the exact iteration at which the two processestake on different behavior.

Figure 17-3. Development of the logistic function for f0=.95

and f0=.949999999 (blue) for c=3.75 in both cases. This

minimal variation of the initial value yields dramaticallydifferent sequences for n>30.

Page 179: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Bifurcation

The behavior of the logistic map does not depend only on the value of f0 alone

but also on the constant c. In fact, for numerous values of c, f0 does not mattermuch at all since the function will eventually converge. Most obviously, setting

c=0 yields zero values for all values of fn for n>0. (Figure 17-4). Similar

behavior results for all values of c<~3.0. At this point, the map converges to

several attractors again, depending on the value of the initial f0. It converges

first to 2 attractors, then to 4, 8, 16, and so on until it reaches chaotic behavior.(This repeated doubling within families of functions such as the logistic maphas been first noted and extensively studied by the physicist Mitchell J.Feigenbaum.) Interestingly, the sequence of functions of the logistic maptraverse several states of temporary tranquility on their way to sheer chaotic

behavior. Thus, fn is actually a family of functions of the form y(s,c) = c * y * (1 -

y), with seed values s = {0.0005, 0.001 ... 1.0}. Although the logistic map maygive radically different numerical sequences for any two functions of the same

family (functions with equal c but differing seed value s) the distribution pattern

of a family after i iterations and the histogram of any of its functions up to the ithiteration approach the same attractor.

Figure 17-4. Bifurcation of the logistic map. Values for c

are plotted on the x-axis range from 0.0 to 4.0 and are

mapped onto the distribution of values of f(n) after 1000

iterations starting from 2000 equally distributed initial seedvalues of fn. Ranging from black over blue to white, colors

represent statistical density.

Page 180: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Mapping the Logistic Map

We now sonify the logistic map by using rescale to map iterative values ontosound parameters.

Example 17-1. The Logistic Map as key numbers

(define (logmap chaos len rate dur key1 key2 amp) (let ((y (random 1.0)) (k 0)) (process repeat len set y = (* y chaos (- 1 y)) set k = (rescale y 0 1 key1 key2) output (new midi time (now) :keynum k :duration dur :amplitude amp) wait rate)))

The logmap process maps values from the Logistic Map onto key numbersbetween key1 and key2. The initial conditions f0 are represented by the

specified chaos parameter, which may range between 0 less than 4.0, and theinitial random value for y.

Interaction 17-1. Listening to the Logistic Map

cm> (events (logmap 3.7 200 .125 .25 60 96 .6) "logmap.mid")"logmap-1.mid"cm> (events (logmap 3.99 200 .125 .25 60 96 .6) "logmap.mid")"logmap-2.mid"cm>

→ logmap-1.mid→ logmap-2.mid

The second example increases the chaos factor close to the maximum. In thenext example we explore chaotic rhythms.

Example 17-2. Chaotic rhythms

(define (groove chaos len pulse) (let ((y (random 1.0))) (process repeat len set y = (* y chaos (- 1 y)) output (new midi :time (now) :keynum 60 :duration .01) wait (* pulse y))))

Page 181: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 17-2. Listening to the Logistic Map

cm> (events (groove 3.99 50 .5) "logmap.mid")"logmap-3.mid"cm>

→ logmap-3.mid

This last example links rhythms and pitches together to form more distinctivegestures. The rhythms are quantized to increments of .1 using Common Music'squantize function.

Example 17-3. Two dimensional Logistic Map

(define (logmap-2d chaos len pulse key1 key2 dur) (let ((y (random 1.0))) (process repeat len set y = (* y chaos (- 1 y)) output (new midi :time (now) :keynum (rescale y 0 1 key1 key2) :duration dur) wait (quantize (rescale y 0 1 0 pulse) .1))))

Interaction 17-3. Listening to logmap-2d

cm> (events (logmap-2d 3.99 200 .125 60 96 1) "logmap.mid")"logmap-4.mid"cm>

→ logmap-4.mid

Op. 6 No. 2, The Henon Map

The Henon Map is given by the two-dimensional function:

H(x,y) = ((y + 1) - ax2,bx)

where a and b are constants, typically 1.4 and 0.3, respectively. The HenonMap is the two-dimensional analog to the logistic function in that it has an

oscillating catalytic term y+1=bx+1 and an inhibiting part -ax2 (Figure 17-5).

Figure 17-5. The Henon Map for standard values a=1.4

and b=.3 after 5000 iterations.

Page 182: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

We can define two processes to map the Henon into musical space. The firstmaps Henon values onto frequency, the second does this in two voices.

Example 17-4. Henon Mapping

(define (henon1 len rate dur key1 key2) (let ((x 0) (y 0)) (process repeat len for z = (- (+ y 1) (* .2 x x)) set y = (* .99 x) output (new midi :time (now) :keynum (rescale y -5 5 key1 key2) :duration dur) wait (rescale z -5 5 0 rate) set x = z)))

(define (henon2 len rate key1 key2) (let ((x 0) (y 0) (k 0) (r 0)) (process repeat len for z = (- (+ y 1) (* .2 x x)) set y = (* .99 x) set k = (rescale y -5 5 key1 key2) set r = (rescale z -5 5 0 .4) output (new midi :time (now) :keynum k :duration r) sprout (new midi :time (+ (now) rate) :keynum k :duration .05) wait (+ rate r) set x = z)))

Page 183: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Interaction 17-4. Listening to Henon Mapping

cm> (events (henon1 250 .24 1 48 84) "henon.mid")"henon-1.mid"cm> (events (henon2 250 .25 48 84) "henon.mid")"henon-2.mid"cm>

→ henon-1.mid→ henon-2.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file chaos.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

References

Kramer, G. (????). Sonification Report: Status of the Field and ResearchAgenda [On-line]. Availablehttp://www.icad.org/websiteV2.0/References/nsf.html.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 184: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

18Randomness and ChanceComposition

There is divinity in odd numbers, either in

nativity, chance, or death.

— William Shakespeare

Music composition, and artistic expression in general, often reflects a tensionbetween rational and irrational processes that underlie and motivate creative work.Perhaps more than at any other time in history, the 20th century witnessed therational and irrational taking explicit shape in the techniques that composers usedto create music. It should not be surprising that the same century that gave birth toQuantum Mechanics and Heisenberg's Uncertainty Principle also found artists

consciously incorporating non-deterministic, or random processes into theirartistic pallettes. One of the most important expressions of this influence was in the

chance procedures adopted by some artists. Whether it is motion added to adance by Merce Cunningham, an abstract painting by Jackson Pollock or the IChing music of John Cage, randomness and chance are consciously embraced asgenerative techniques for making art. An interest in chance procedures alsoserved as an important motivation in the development of the experimental musictradition in the 1950's, from which computer composition stems. The earliestexamples of computer composition by Lejaren Hiller are largely based on somechance procedures that will be discussed in the next two chapters. thesetechniques can have a profound effect on the compositional process yet are verysimple to perform with a computer. The computer also enables composers toexperiment with many different types of randomness that are more interesting thanthe simple “coin tossing” model that ever composer is familiar with. It perhapssomewhat ironic then, that the computer programs that composers usually use tocreate chance compositions are not really generating randomness at all!

Random Number Generators

Computers are designed to function in a totally consistent and reproduciblemanner. This is a wonderful feature when your plane is landing or your tax returnsare prepared but it means that randomness is inherently difficult to achieve withsuch a deterministic machine. Although some applications use specializedhardware to generate truly random signals, more typical computer programs only

simulate randomness with a special type of algorithm called a pseudo-randomnumber generator. Pseudo-random number generators are software programs that

Page 185: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

calculate a series of numbers with statistically random properties. But, as the namesuggests, these numbers are not really random at all. A pseudo-random generatoris a completely deterministic algorithm that generates one series of numbers. Acommon design of many random generators uses past output value as the seedfor calculating the next number in the series. This means that each time thegenerator starts from a particular seed the exact same sequence of numbers in theseries will ensue. Many random number generators, including Lisp's randomfunction, allow the user to specify an initial random state seed that positions thealgorithm at a certain location in its sequence of numbers. Composers who want toexpress randomness “philosophically” in a composition will therefore want to usedifferent initial seeds each time a chance program runs to insure the programgenerates a different outcome each time. On the other hand, composers oftenwant just the opposite: to save seeds in order to “back up” in the event that arandom process has produced particularly satisfying results! This latter method isone way to implement the common generate and test compositional style incomputer composition. By building random choice into the algorithms thatgenerate music the composer can generate many possible "drafts" of a piece,each with its own random variations. These versions can then compared todetermine which one(s) best satisfy the composer's goals. Sometimes a wholepiece is generated in this manner, other times the composer collects the bestfeatures from different drafts and combines them into a final version. Since theinitial seed for each draft was saved the composer can regenerate the bestversions at any time, as long as the program code remains exactly the same.

Since the material in this book assumes that the reader is interactivelyexperimenting with examples, we are virtually assured that the book's path throughLisp's random number series will not be repeated by the reader. For this reasonnone of the examples in this book make use of random number seeds. But byestablishing different seeds each time you begin to work, or by saving the currentseed before you run a chance program, you can minimize (or maximize) thelikelihood that your program will generate the same results. To learn more aboutthe process of seeding in Lisp consult the documentation on the random function inthe Language specification.

Probability

A random process is a process in which outcomes have only a certain likelihood of

happening. Probability is a measurement of the likelihood of these events actuallyoccurring. Probability in our daily lives is often described in terms of a “percentagechance”. For example, the weatherman might say: “There is a 25% chance that itwill rain today”. The percentage indicates that probability is distributed over all theevents in a random process, where each event, or range of events, has a certainproportion of the total probability. So, although we do not know for certain if it willrain or not, probability allows us to infer several facts from the forecaster'sstatement:

1. there is a 25% chance that it will rain today2. there is a 75% chance that it will not rain today3. there is a 100% chance that it will either rain or not rain today4. there is a 0% chance that neither will happen

Weather forecasts notwithstanding, probability factors are typically expressed asvalues between zero and one rather than as percentages between 0 and 100. The

Page 186: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

[Function]

[Function]

[Function][Function]

[Function]

[Function]

[Function]

probabilities of all the events in a random process add up to 1.0 (100%) becausethere must be absolute certainty (1.0) that at least one of the outcomes will happen.

Some random processes are characterized by an equal distribution of

probability over all of the events in the process. This is called a uniform probability

distribution. For example, when a fair die is rolled there is an equal likelihood thatany one of the six sides will land face up. Since the total probability must add up to1 and there is an equal chance for any side to land on top, each of the six sides

has a probability factor of 1/6. For a given event x, its probability, P(x), is the ratioof the number of ways the outcome can happen to the total number of possible

outcomes. For example, if x is a randomly chosen key number on a piano thenP(x)= 1/88. If x is a randomly chosen black key on the piano, then there are 36

possible ways to do this and so P(x)=36/88=9/22. Similarly, the complementary

probability of an event (all the events that are not x) is 1-P(x). So the probability ofselecting a white key x on the piano can be calculated by complementing theprobability of black keys, or P(x)= 1-9/22=13/22. In a continuous distribution,probability is defined over an area under a distribution curve. This means that theprobability of any single event, or “point” is zero since it has no area. This soundsodd but since probability is the ratio of the number of ways an event can happen tothe total number of events then if there are an infinite number of events theprobability of any single event is zero.

Lisp's random function generates numbers in both discrete and continuousdistributions. By specifying an integer range to random only integers will bereturned. If the range is expressed as a real, or floating point value, then continuousvalues within the limits of floating point precision are returned. The random functionalways returns a value between zero less than the bounds specified to the function.To generate any other range of values, or to randomly generate non-numericvalues, requires that the output from random be scaled and offset, or used asindices into lists of non-numeric data. These kinds of manipulations are such acommon occurrence in composition that Common Music provides a number ofdifferent functions to facilitate chance operations in musical contexts. Thesefunctions are listed together here and then discussed at appropriate places later inthis chapter.

(between low high [skip])

(odds prob [then] [else])

(pick ...)(pickl list [:start i] [:end i] [:skip x])

(ran [:type k] [:a n] [:b n])

(shuffle list [:start i] [:end i])

(tendency n env1 env2 [:offset n] [:scale n])

(vary n pct [where])

Probability Distributions

The probability of events in a random process is determined by a probability

distribution — a mathematical function that describes the likelihood of all possible

outcomes. Distributions can be characterized by a mean or average, value thatthey produce and by a characteristic “shape” that the probability expresses overthe events in the random process. One of the great advantages to using acomputer to work with random processes is that it is possible for a composer toemploy complex distributions without needing to understand the underlying

Page 187: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

mathematics of their implementation (which can be quite complex for some typesof randomness). Composers who are interested in a formal presentation ofrandom number generation should read Knuth's Seminumerical Algorithms,volume 2 of The Art of Computer Programming (Knuth) , which contains thedefinitions of many random number generators with different probabilitycharacteristics. Computer Music (Dodge) by Charles Dodge and Thomas Jersealso contains an excellent discussion of probability theory and random numberdistributions.

In the next few sections we define the characteristics of some commondistributions. Each example includes a graphical plot of sampled events together

with a histogram that depicts a characteristic density of events that eachprobability distribution generates. The histograms were generated by analyzing theoutput from a musical process that maps random values onto a range of integerMIDI key numbers. The musical processes that generated the results are shown ineach example. Such a process can be listened to in “discrete” mode — quantizedto the closest MIDI key number — or in “continuous” mode, by using the channeltuning methods discussed in Chapter 14 to output microtonal frequency values.Example 18-1 contains the basic process definition we will use to sonify thedistributions. The sound example provided for each distribution plays thedistribution in continuous mode.

Example 18-1. Mapping distributions onto frequency space.

(define (play-ran type len rate key1 key2) (process repeat len for r = (ran :type type) for k = (rescale r 0.0 1.0 key1 key2) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude .5 ) wait rate))

;;; channel tuning on first 9 channels

(define ct9 (list true 0 8))

(define (mean-ran type n a b) (/ (loop repeat n sum (ran :type type :a a :b b)) n))

The play-ran function in Example 18-1maps values from Common Music's ranfunction onto a range of key numbers specified to the process. The ran functiongenerates random numbers in a distribution specified using its :type keywordargument. The following distribution types are supported:

:uniform :low :high :triangular :beta :exponential :cauchy

:poisson :gamma :gaussian

If the :type argument is not specified then the uniform distribution is generated.See the documentation on ran in the Common Music Dictionary for moreinformation about this function.

The mean-ran function calculates the mean, or average, value of n outcomes of

Page 188: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

ran. This gives us some measure of how well the pseudo-random numbergenerator approximates the theoretical mean value of each distribution type.

Uniform distribution

In a uniform distribution all events have an equal likelihood of being selected. This

means that in a discrete selection over n events, each event has 1/n probability ofbeing selected. In a continuous distribution the events under two equal portions ofthe total probability range will have an equal likelihood of selection with a meanvalue of .5. Lisp's random function return numbers in a uniform distribution. Thesevalues can then be filtered or processed in different ways to produce non-uniformbehavior.

Figure 18-1. Two plots depicting values (points) andhistogram (bars) of uniform distribution. The Y axis plots

continuous random values normalized 0-1. The x axis plots

the histogram range defined over 127 key numbers.

Interaction 18-1. Playing continuous uniform distribution.

cm> (events (play-ran :uniform 100 .1 20 100) "uniform.mid" :channel-tuning ct9)"uniform-1.mid"cm>

→ uniform-1.mid

Linear "Low-pass" Distribution

In a linear “low-pass” distribution the probability of larger numbers decreaseslinearly over the range of values returned by the process. The low-pass behavior isimplemented by filtering a uniform process such that the minimum of two uniformlychosen numbers is returned:

(min (random 1.0) (random 1.0))

The expected mean value of the low-pass distribution is .2929.

Figure 18-2. Low pass values (points) and histogram (bars).

Page 189: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 18-2. Playing the low pass distribution.

cm> (events (play-ran :low 50 .1 20 100) "low.mid" :channel-tuning ct9)"low-1.mid"cm>

→ low-1.mid

Linear "High-pass" Distribution

In a linear “high-pass” distribution the probability of larger numbers increaseslinearly over the range of values. The distribution can be implemented by returningthe maximum of two uniform random numbers:

(max (random 1.0) (random 1.0))

The expected mean value of the high distribution is .2929.

Figure 18-3. High pass values (points) and histogram (bars).

Interaction 18-3. Playing the high pass distribution.

cm> (events (play-ran :high 50 .1 20 100) "high.mid" :channel-tuning ct9)"high-1.mid"cm>

→ high-1.mid

Page 190: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Mean Distribution

In a mean distribution the probability of numbers in the middle of the distributionincreases linearly. A mean distribution can be implemented by averaging twouniform random numbers:

(/ (+ (random 1.0) (random 1.0)) 2))

The expected mean value of the distribution is .5.

Figure 18-4. Triangular distribution values (points) and

histogram (red bars).

Interaction 18-4. Playing the triangular distribution.

cm> (events (play-ran :triangular 50 .1 20 100) "tri.mid" :channel-tuning ct9)"tri-1.mid"cm>

→ tri-1.mid

Exponential Distribution

The exponential distribution returns the natural logarithm of a uniformly chosennumber.

(/ (- (log (- 1 (random 1.0)))) a))

The a parameter provides a “stretching factor” that scales values closer or further

from 0; the larger the value of a the more the values are pushed toward zero. Ifrandom returns 0 then loge1 is 0, the minimum value in the distribution. As random

values exceed 1/e (0.367) the values returned in the distribution exceed 1.0. Thereis no upper limit to values produced in the exponential distribution, but 99% of thetime its value is less than 6.908.

Figure 18-5. Exponential distribution values (points) andhistogram (red bars) for a=1 and a=2.

Page 191: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The expected mean value of the exponential distribution for a=1 is .693.

Example 18-2. An exponential distribution.

(define (play-exp len rate key1 key2 a) (process repeat len for r = (ran :type :exponential :a a) for k = (rescale r 0.0 6.908 key1 key2) when (< k 128) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude .5 ) and wait rate))

The play-exp process passes the a stretching factor to the ran function. Since thedistribution is unbounded the conditional clause insures that only valid MIDI keynumbers will be output.

Interaction 18-5. Playing the exponential distribution for a=1.

cm> (events (play-exp 50 .1 20 100 1) "exp.mid" :channel-tuning ct9)"exp-1.mid"cm>

→ exp-1.mid

Gaussian Distribution

The Gaussian, or normal, distribution returns values that fall under the normal curve.

The spread, or standard deviation is 1.0 centered at 0, so 68.26% of the resultsare between -1 and 1, and 99.74% of the results are between -3 and 3.

Figure 18-6. Gaussian distribution values (points) and

histogram (bars).

Page 192: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 18-3. A Gaussian distribution.

(define (play-gauss len rate key1 key2) (process repeat len for r = (ran :type :gaussian) for k = (rescale r 0.0 3 key1 key2) when (<= 0 k 127) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude .5 ) and wait rate))

Interaction 18-6. Playing Gaussian distribution.

cm> (events (play-gauss 50 .1 20 100) "gauss.mid" :channel-tuning ct9)"gauss-1.mid"cm>

→ gauss-1.mid

Beta Distribution

The Beta distribution is one of the most musically interesting distributions to workwith because it really describes a whole family of distribution shapes. The beta

distribution is controlled by two parameters a and b. When a=b=1 then a uniformdistribution results. When a=b, the distribution is symmetric around .5. When a<1

and b<1 the density of large and small numbers increases. When a>1 and b>1density is similar to the Gaussian distribution.

Example 18-4. The beta distribution.

(define (play-beta len rate a b key1 key2) (process repeat len for r = (ran :type :beta :a a :b b) for k = (rescale r 0.0 1.0 key1 key2) output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude .5 ) wait rate))

Page 193: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Figure 18-7. Beta distribution values (points) and histogram(bars).

Interaction 18-7. Playing the beta distribution for a=b=.4.

cm> (events (play-beta 50 20 90 .1 .4 .4 ) "beta.mid" :channel-tuning ct9)"beta-1.mid"cm>

→ beta-1.mid

Random Processes in Music Composition

Both continuous and discrete random processes are used in computercomposition to control the evolution of parameterized data. But — as may benoticed in the preceding examples — simply mapping random distributions directlyonto sound parameters does not really translate into interesting musical results.The lack of correlation between events in a random process causes our interest todiminish rapidly as we listen. There are really only two ways to deal with this issue.One method is to reserve the use of randomness for aspects in a composition thatdo not affect its structural integrity. The other method is to control randomness insome manner such that the listener either perceives an underlying structureunfolding or at least has some expectation of regularity, or correlation in thepattern. The remaining sections of the chapter present different approaches tocontrolling random processes such that they generate more interesting musicalresults than the basic distributions on which they are based.

Sampling Without Replacement

Generating discrete random values using an expression like (random 88) is calledsampling. Sampling can be performed two different ways. Sampling with

replacement means that once a value has been sampled it is still available to besampled again. For example, if key number 60 is generated by (random 88) then

60 can be still be selected as a possible next value. Sampling withoutreplacement describes a process in which generated values are not returned tothe population once they have been selected. This is similar to how cards are dealtfrom a shuffled deck. By using sampling without replacement, a composerrandomly reorders a set of specific values whose relationships are otherwise fixed.This reordering can be perceived as a kind of “freshness” in the data. The function

Page 194: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

shuffle can be used to implement sampling without replacement. Shufflerandomly permutes a list of values, which can then be sampled one at a time toproduce a random ordering without repetition. Since shuffle operates on listpositions rather than values, the list can hold any type of data.

Example 18-5. Sampling without replacement.

(define (sampling reps rate notes ) (let ((len (length notes))) (process for i below reps for j = (mod i len) when (= j 0) ; need to reshuffle? set notes = (shuffle notes) output (new midi :time (now) :keynum (list-ref notes j) :duration (* rate 1.5) :amplitude (interp j 0 .4 (- len 1) .8)) wait rate)))

Our sampling process also produces a crescendo that starts over again each timethe values are shuffled so that each “period” in the population can be easily heard.Note that while shuffling avoids direct repetition within the period, there may still bea direct repetition between the last note of a period and the first note of the next.

Interaction 18-8. Calling shuffle.

cm> (events (sampling 80 .1 '(c d ef f g a bf c5)) "shuffle.mid")"shuffle-1.mid"cm>

→ shuffle-1.mid

Direct reselection can be an issue anytime discrete random selection is used.When the range of random values is sufficiently large direct repetition may be soinfrequent that it does not have a noticeable effect on the results. But as thenumber of events in a discrete process diminishes the likelihood of directreselection increases. At some point this may cause an unwanted regularity inmusical results. One way to deal with this is to use the optional omit argument to

the between function to avoid direct reselection. An omit value will not be returned

by between. By specifying the last output value as the omit value for the nextrandom number, direct reselection will not occur. The following process can beused experiment with the effects of allowing or inhibiting direct reselection.

Example 18-6. Avoiding direct repetition in discrete

distributions.

(define (reps len lb ub skip?) (process repeat len for l = (if skip? k false) for k = (between lb ub l) output (new midi time (now) :keynum k

Page 195: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:duration .2) wait .125))

If skip? is true then the variable l is set to k, the last value returned by between. In

this case l is not false, so when it passed as the omit value to between direct

repetition of l cannot occur. If skip? is false then l will be false no matter what thevalue of kis and direct selection is then possible.

Interaction 18-9. Random selection with and without directrepetition.

cm> (events (reps 30 60 63 false) "reps.mid")"reps-1.mid"cm> (events (reps 30 60 63 true) "reps.mid")"reps-2.mid"cm>

→ reps-1.mid→ reps-2.mid

Variance

Continuous selection is commonly used to provide variance in scaling andoffsetting procedures. For example, by scaling a regular tempo or pulse value by asmall random percentage the actual rate of events will fluctuate in time andproduce a more natural effect than a fixed rate would. If the percentage variance issmall enough the fluctuations will not affect the perception of an underlyingstructural pulse. The process definition in Example 18-7 can be used to experimentwith random percentage variance applied to the pulse, amplitude and duration ofevents.

Example 18-7. The tapping process.

(define (tapping reps pct rate key amp) (let ((half (/ reps 2))) (process for i below reps for v = (if (< i half) 0 pct) output (new midi :time (now) :duration (vary .1 v ) :keynum key :amplitude (vary amp v :below)) wait (vary rate v))))

The tapping process plays a single key number reps times at a specified rate andamplitude. The events in the first half of the process are played with fixed valuesand in the second half the values are allowed to vary by up to pct amount of theirvalues. By default the function vary distributes variance equally on either side ofthe fixed value. To produce variance less than the value specify :below as the thirdargument to vary. Similarly, the keyword :above will offset the variance above thevalue.

Page 196: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 18-10. Comparing 10% variance with 50%variance.

cm> (events (tapping 50 .1 .2 72 .6) "tap.mid")"tap-1.mid"cm> (events (tapping 50 .5 .2 72 .6) "tap.mid")"tap-2.mid"cm>

→ tap-1.mid→ tap-2.mid

Even something as simple as random variation of a beat can be made musicallyinteresting if it is controlled in some manner. In the next example the amount ofvariation applied to the beat is controlled by an envelope. When the envelope is at0 then no variation occurs, when the envelope is at 1 then full variance isexpressed. By using an envelope to control random variance the process caninterpolate over the full continuum between completely random time incrementsand a strictly measured pulse. The same envelope that controls the beat will also

be used to provide a value for the a and b beta distribution parameters thatgenerates amplitude values for the process. When the control envelope is at zero

(no random variance) then the beta distribution will be a=b=.4 and produce a largepercentage of loud and soft values (Figure 18-7). When the envelope is at 1 thenthe beta distribution will be a=b=1 and produce values in the uniform distribution.When the control envelope is at zero (no random variance) then the beta

distribution will be at (beta a=b=1) and produce values in the uniform distribution.

When the envelope is at 1 (full random variance) then a=b=.4) and a largepercentage of loud and soft values are produced. The shape of the envelopedefined in Example 18-8 causes the process to begin and end with random timevalues and express the strict pulse in the middle.

Example 18-8. The rain process.

(define (rain len rate env key1 key2 amp) (process for i below len for e = (interpl (/ i len) env) for v = (vary rate e :above) ;; rescale e to control shape of beta distribution ;; when e=0 z=.4, and when e=1 z=1. for z = (rescale e 0 1 1 .4) for b = (ran :type :beta :a z :b z) output (new midi :time (+ (now) v) :duration v :keynum (between key1 key2) :amplitude (rescale b 0 1 .1 amp)) wait rate))(define rain-env '(0 1 .4 0 .6 0 1 1))

We listen to three simultaneous versions of the rain process, each with differentlength beats.

Interaction 18-11. Listening to rain.

Page 197: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (events (list (rain 80 .5 rain-env 70 90 .8) (rain 40 1 rain-env 40 60 .8) (rain 20 2 rain-env 20 40 .9)) "rain.mid")"rain-1.mid"cm>

→ rain-1.mid

Shape-based composition

By applying a random variance to envelope values we can generate randomvariations on the basic shape of the envelope:

Example 18-9. Adding a random variance to envelope values.

(define (shaper len rate env key1 key2) (process for i below len for e = (interpl (/ i len) env ) for k = (rescale e 0 1 key1 key2) for r = (between -5 5) output (new midi :time (now) :keynum (+ k r) :duration rate) wait (* rate (pick .5 .25 1))))

(define shape-env '(0 0 .2 1 .4 .25 .6 .5 .8 0 1 .5))

The process in Example 18-9 scales and offsets values from an envelope togenerate a range of key numbers specified to the process. A random amount isthen added to the the key number to shift it upwards or downwards.

Interaction 18-12. Listening to two variations of the sameenvelope.

cm> (events (list (shaper 40 1 shape-env 70 90) (shaper 40 1 shape-env 60 80) (shaper 20 2 shape-env 60 48)) "shape.mid")"shape-1.mid"cm> (events (list (shaper 40 1 shape-env 70 90) (shaper 40 1 shape-env 60 80) (shaper 20 2 shape-env 60 48)) "shape.mid")"shape-2.mid"cm>

→ shape-1.mid→ shape-2.mid

Tendency Masks

Page 198: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

A tendency mask is a technique related to the shape-based approach firstdescribed by the composer Gottfried Michael Koenig. A tendency mask is createdusing two envelopes to specify the lower and upper boundaries for a randomselection. By describing each boundary as an envelope the range of the randomvariable can vary as a function of some dynamic process. Common Music's

tendency function returns a random value given an x lookup value, and a lower andupper envelope that define the boundaries.

Example 18-10. Tendency masking.

(define low-mask '(0 .4 .75 .4 1 0))(define top-mask '(0 1 .25 .6 1 .6))

(define (masker len rate lower upper key1 key2) (let ((last (- len 1))) (process for i to last for e = (tendency (/ i last) lower upper) for k = (rescale e 0 1 key1 key2) output (new midi :time (now) :duration rate :keynum k) wait rate)))

In this example two envelopes lower and upper control the range of random

selection. The variable e is set to a random value between an interpolated lowerand upper bounds in the two envelopes specified to tendency This random value,which lies between zero and one, is then rescaled to lie between the key numbers

key1 and key2 passed as arguments to the process.

Interaction 18-13. Listening to masker.

cm> (events (masker 80 .1 low-mask top-mask 20 110) "masker.mid")"masker-1.mid"cm>

→ masker-1.mid

Weighted Random Selection

In weighted random selection individual probability weights are assigned to eachevent in a discrete random process. This affords more precise control over theoverall mix of outcomes than would otherwise be possible using one of thedistributions already discussed in this chapter. Common Music's odds functionimplements a simple binary form of weighted random selection that is identical tothe chance? function from Chapter 7 (except that values other than true and falsecan be returned by odds). More complex forms of weighted selection arepresented in Chapter 19 and Chapter 20, but these discussions assume anunderstanding of probability tables, which we present here.

Probability Tables

Page 199: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

A probability table associates each outcome in a discrete random process with aspecific portion, or segment, of the total probability range. The size of eachsegment reflects the proportional likelihood of that value being generated from therandom process. To generate the next value from a table, a uniformly distributedrandom value is generated and the table is searched to find the probabilitysegment that contains the random lookup value. The datum associated with thatsegment is then returned from the table as the random value. The implementationdefined in Example 18-11 allows us to specify probabilities in terms of relative

weights that are associated with each possible outcome in the random process.The function that creates the probability table will convert these relative weights intoabsolute portions of the total probability interval 0 to 1. To see how this works,consider a weighted random process that generates four rhythmic symbols: S, E,Q and H where S should be three times as likely Q, E should be twice as likely asQ and H should 1/4 as likely as Q (Table 1).

Table 18-1. Example probability table for four rhythmicoutcomes.

index 0 1 2 3

outcome s e q h

weight 3 2 1 1/4

segment 0.0-0.48 0.048-0.8 0.8-0.96 0.96-1.0

Our implementation of probability tables (ptables) in Example 18-11 uses twofunctions. The first function make-ptable creates a ptable from a list of outcomesand weights. The pran function then generates a random outcome from a ptablecreated by make-ptable.

Example 18-11. Implementing probability tables.

(define (make-ptable data) (let ((total (loop for d in data sum (second d))) (sum 0)) ;; total holds sum of weights in data (loop for d in data for v = (first d) ; outcome to return for w = (second d) ; relative weight do (set! sum (+ sum w)) ;; collect outcome and normalized probability collect (list v (/ sum total)))))

(define (pran table) ;; return outcome in table according ;; to its weighted probability (let ((x (random 1.0))) ;; x is uniform number < 1. (loop for d in table for p = (second d) when (< x p ) ; x in this segment. return (first d))))

A probability table is represented as a list of lists, the first element of each sub-list

Page 200: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

is an outcome and the second value is its weight relative to the other outcomesdefined in the list. For example, the probability table described in Table 1 would bespecified as the list:

((s 3) (e 2) (q 1) (h 1/4))

The make-ptable function creates a probability table from data, a list of values andweights as shown in the example above. The table returned by make-ptable is

identical to data except that the relative probability weights in the data descriptionare converted to equivalent absolute segments in along the probability interval 0-1.

The variable total is set to the sum of the relative weights in data. The loop theniterates over each sublist d in data and sets the variable v to the outcome of d and

sets w to its weight. The loop then collects lists containing the same value v but with

a weight normalized to lie between 0-1 by calculating the current ratio of sum to thetotal weight. The table returned by the function contains the same outcomes as

data but with relative weights converted to monotonically increasing values thatrepresents the upper bounds of each segment along the probability range 0-1.

The function pran implements weighted random selection given a probabilitytable. It generates a uniform random number x between 0 and 1. The largersegments in the table will contain proportionally more chances of containing values

of x than the smaller segments. The loop iterates over each element in the tableand compares x to the probability segment value p of each segment. Since

segment values are stored in increasing order, the first occurrence of x less than pis the probability segment that contains the value to return.

Given our implementation of probability tables we can now define a process togenerate values using weighted random selection:

Example 18-12. Weighted random selection of notes andrhythms.

(define note-tabl (make-ptable '((c 3) (d 1) (ef 2) (f 1) (g 3) (af 1) (bf 1) (c5 3) )))

(define rhy-tabl (make-ptable '((s 3) (e 2) (q 1) (h 1/4))))

(define (play-pran reps notes rhys tempo) (process repeat reps for k = (keynum (pran notes)) for r = (rhythm (pran rhys) tempo) output (new midi :time (now) :keynum k :duration (* r 1.5)) wait r))

The probability table defined in notes “prefers” notes of a C-minor triad in the C-

minor scale. The table stored in rhys contains the example shown in Table 1.

Interaction 18-14. Listening to Pran

cm> (events (play-pran 50 note-tabl rhy-tabl 100) "ptab.mid")"ptab-1.mid"cm>

Page 201: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

→ ptab-1.mid

Random Walks

The term random walk describes a random process in which a value is

incremented by a random amount. The motion produced by a random walk isreminiscent of how a drunk staggers around when walking: with each stagger thelocation of the drunk changes randomly within the radius of one step. The randomwalk is also characteristic of how molecules and very small particles move

(Brownian motion) and for that reason it is sometimes referred to as brown noise.Because the next value in a random walk must lie within a certain interval from

the current value, the current value has a very strong influence on the next outcome.

The influence of past values on successive values is called correlation. The

correlation in a random walk is characterized by a 1/f2 spectrum, which means thatsmall rates of change are much more present than larger ones. This is a very

different behavior than the uniform distribution, or white noise, which exhibits largechanges just as frequently as small ones because there is no correlation betweensuccessive values.

A random walk is easy to implement in a musical algorithm. A walking value xis initialized to some appropriate starting value and then incremented by a randomamount (positive or negative) on each iteration of the process. A moment'sreflection will tell us that at some point an unbounded walk will likely exceed thelimits of whatever sound parameter it is controlling. For this reason the random

walks that control parameterized data are typically bounded, or constrained, insome manner so that they always lie within minimum and maximum boundaryvalues. There are no hard and fast rules on how to adjust values that wander pastboundaries: common adjustments include resetting value to the boundary value,reflecting the value back into bounds, randomly repositioning the value somewherewithin the range, or stopping the process altogether.

Common Music's drunk function implements bounded or unbounded randomwalks and its optional keyword arguments constrain values to lie within specified:low and :high boundaries. A value that exceeds either bounds is adjustedaccording to the :mode argument. Possible modes are:

:reflect :limit :reset :jump :stop

The default mode is :reflect, which “wraps” a value back into bounds by itscurrent excess amount. Example 18-13 defines a process that uses drunk togenerate a random melody.

Example 18-13. Random walks applied to keynum and

amplitude.

(define (stagger len low high step) (let ((k (/ (- high low) 2)) (a (between .1 .9))) (process repeat len for r = (pick 1/8 1/4) set k = (drunk k step :high .9 :mode :jump) output (new midi :time (now) :duration (* r 1.5)

Page 202: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:keynum k :amplitude a) wait r)))

K is a key number controlled by a random walk between key1 key2. On each

iteration of the process the key number can deviate by up to step steps from its

current position. The variable a is the amplitude of each event, which is allowed towander between .1 and .9. The :mode keyword argument controls the boundary ruleof the walk. The value :jump causes a value that is out of bounds to be reset to arandomly chosen position within bounds.

Interaction 18-15. Listening to the stagger process.

cm> (events (stagger 50 40 80 3) "drunk.mid")"drunk-1.mid"cm> (events (stagger 50 20 90 7) "drunk.mid")"drunk-2.mid"cm>

→ drunk-1.mid→ drunk-2.mid

Example

The final example in this chapter was provided by Michael Klingbeil from somestudies he made for an orchestra piece. The example generates a series ofexponential gestures, each of which consists of an upward “arc” of microtonalnotes. This short example uses many of the functions presented in this chapter aswell as the exponential scaling functions from Chapter 16 and the harmonic seriesfrom Chapter 15.

Example 18-14. Using randomness in scaling and tuning.

(define (arpeggiate-exprhy keynums time rate midpoint-frac amplow amphi legato bass-legato bass-cutoff last-legato) (let* ((segs (length keynums)) (last (1- segs)) (midpoint (floor (* segs midpoint-frac))) ;; deltas below midpoint follow one curve, above another. (deltas (append (explsegs midpoint (* midpoint-frac time) rate) (explsegs (- segs midpoint) (* (- 1 midpoint-frac) time) (/ 1 rate))))) (process for i from 0 for k in keynums for d in deltas for r = (if (< k bass-cutoff) bass-legato (if (= i last) (* last-legato d) (* legato d))) for a = (between 0.45 0.5) then (drunk a 0.1 :low amplow :high amphi )

Page 203: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

output (new midi :time (now) :keynum k :amplitude a :duration r) wait d)))

The argeggiate-exprhy process creates both upward and downward “arcs” ofMIDI events from a specified set of key numbers. The rhythms of each arc aredetermined by dividing a total time duration into two segments of exponentially

related rhythms. The midpoint-frac is a proportion that determines the duration of

each are relative to the total time specified to the process. This duration is then“chopped up” into exponentially related time values that sum to the duration of the

arc. The amplitude a for the each event is determined by a random walk. The

amplow and amphi parameters control the overall range of the random walk. Bass-

legato sets the duration of notes with keynum less than bass-cutoff. This allows thebass notes to ring even when the synthesizer does voice stealing for the middle

notes of the arpeggio. Last-legato extends the duration of the final note of anarpeggio for similar reasons.

The definition of argeggiate-exprhy produces a single upward and downwardarpeggiation. We now define another process that schedules a series ofoverlapping arpeggiations whose key numbers are determined by distorting theharmonic series:

Example 18-15. Arpeggiated harmonic distortions.

(define (distort-harmonics fund distort) (loop for h from 1 below (floor (/ 25.0 distort)) if (odds (* 0.9 distort)) collect (keynum (* fund (expt h distort)) :hertz)))

(define (arpa-harmonic note dur gap) (process with fund = (hertz note) for distort from 0.7 below 1.05 by 0.05 for notes = (distort-harmonics fund distort) sprout (arpeggiate-exprhy notes (* (vary dur 0.1) distort) (between 4.0 0.25) (between 0.3 0.6) 0.3 ; amplow 0.8 ; amphi (* dur distort 0.7) ; bass-legato 2.0 ; legato 59 ; bass cutoff 1.0) ; last-legato wait (vary gap 0.4)))

Arpa-harmonic spawns overlapping arpeggios with maximum mean duration of

dur and a mean gap between arpeggio starts of gap seconds. Each arpeggio iscomposed of frequencies of a distorted harmonic series. The distortion factorvaries from 0.7 (compressed spectrum) to 1.0 (harmonic spectrum) by incrementsof 0.05. The greater the compression, the lesser the odds of selecting an elementof the spectrum. When the distortion reaches 1.0 the spectrum is harmonic and onaverage 90% of the elements will be selected.

Page 204: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

Interaction 18-16. Arpeggiated harmonic distortions.

cm> (events (arpa-harmonic 'g1 7.0 5.0) "arp.mid" :channel-tuning ct9)"arp-1.mid"cm> (events (arpa-harmonic 'g1 6.0 5.0) "arp.mid" :channel-tuning ct9)"arp-2.mid"cm>

→ arp-1.mid→ arp-2.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file chance.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

References

Knuth, D.E. (1981). The Art of Computer Programming. Reading: Addison-Wesley.Dodge, C. & Jerse, T. (1985). Computer Music. New York: Schirmer Books.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 205: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

19Markov Processes

The previous chapter Randomness and Chance Composition introducedrandom number generation and presented some simple strategies forproducing more musical results than directly mapping distributions onto soundparameters. Without additional controls the basic probability distributionssound incoherent due to a lack of correlation between the generated samples.But in a random walk there is a great deal of correlation between successive

events and the results can sound almost too coherent. A Markov process, orMarkov chain, is a type of random process closely related to the random walkthat is capable of producing different degrees of correlation between its pastand future events. In a Markov process past events represent a state, orcontext, for determining the probabilities of subsequent events. The number of

past events used by the process is called its order. In a first order process theprobabilities for choosing the next event depend only on the immediatelypreceding event. In a second order Markov process the probabilities for thenext choice depend on the last two events. A Markov process can reflect anynumber of past choices, including the degenerate case of no past choices. Azero order Markov Process is equivalent to weighted random selection.

Because past choices influence subsequent choices, a Markov processcan model, or mimic, different degrees of variation in patterns of data. Thehigher the order, the closer the process comes to matching the specific patternit models. The pattern on which a Markov process is based can be invented bythe composer or determined by a statistical analysis of data. This data neednot be quantitative in nature. We will examine both methods for creating Markovprocesses later in this chapter.

Conditional Probability and TransitionTables

A Markov process exhibits conditional probability, in which the probability ofthe next outcome in depends on one or more past outcomes. This means thatthe probability distribution of the random process changes dynamically as afunction of the outcomes that the process has already produced. Conditionalprobability is implemented by linking past outcomes with specific probability

distributions for choosing the next event. For two independent events x and y,

the conditional probability P(x|y) is equal to the product of the probability of x

and y alone. For example, the probability of “heads” appearing in a coin toss is

Page 206: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

P(h)=.5. The conditional probability of consecutive heads P(h|h) is equal to theproducts .5*.5=.25. The probability of tossing three heads is .5*.5*.5=.125 andso on. Higher order processes can be described by multiplying theprobabilities stored in a first order table or by explicitly storing past outcomes ina table and then matching these against a history of outcomes to determine theprobability of future outcomes. A table that establishes this linkage is called atransition table. A transition table is really a table of probability tables. Eachtransition represents the probability distribution given a specific set of pastoutcomes. In a first order Markov process there is only one past outcome foreach transition. In a second order process each transition would refer to a pairof past outcomes.

Transition tables are very similar to the probability tables discussed inChapter 18. We can demonstrate their similarities and differences bytransforming the probability table shown in Table 19-1 into a transition table andthen implementing new behavior not possible using a probability table alone.Table 19-1 is a model of a fair coin toss. There are only two possibleoutcomes, h (heads) and t (tails), each of which has an equal probability ofbeing selected. These outcomes appear as columns in the table.

Table 19-1. Probability table for a fair coin toss. Heads is

labeled h and tails t.

h t

0.50 0.50

Table 19-1 can be turned into a transition table by adding transition rows for allpossible outcomes. In first order Markov, each transition row associates asingle past outcome with a probability distribution for generating the nextoutcome. In our example there are only two possible outcomes: h and t and soour table will only have two transition rows. The Markov process acts as a kindof “feed back” loop: random outcomes are generated from the table (labelednext in Table 19-2) and then return to the table as past outcomes (markedpast). The transition row that matches the current past outcome will thengenerate the next outcome according to its particular probability distribution.

Table 19-2. Transition table for a fair coin toss.

next

h t

pasth 0.50 0.50

t 0.50 0.50

The random outcomes produced by Table 19-2 have the same probabilitydistribution as in Table 19-1. The following sequence depicts 20 representativeoutcomes:

t h t t h h h t t t h h t h h h t h t t

Page 207: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Give the transition table Table 19-2 we can now implement two differentpatterns of events that are impossible to produce using the probability table inTable 19-1. These new patterns can be created simply by changing the weightsin the cells, without altering the structure of the table at all. The next table (Table19-3) produces a biased coin toss that always favors heads, particularly if thelast value was tails:

Table 19-3. Transition table for a biased coin toss.

next

h t

pasth 0.75 0.25

t 1.00 0.00

Representative outcomes from Table 19-3:

t h t h h h h h t h h t h h h h h t h h

The weights in Table 19-3 cause tails to always produce heads, and heads tousually produce heads.

In Table 19-4 we implement a “persistent” coin toss in which the currentoutcome (heads or tails) tends to continue.

Table 19-4. Transition table for a persistent coin toss.

next

h t

pasth 0.60 0.40

t 0.20 0.80

Representative outcomes from Table 19-4:

t t h t h t h h h t t t t t t t t h t t

Implementing Transition Tables

Transition tables can be represented by adapting our design for probabilitytables from Chapter 18. Recall that the constructor function make-ptablecreated a probability table from a list of data that associated random eventswith their relative weights in the table. We start by observing what theseprobability lists would look like for the transitions in Table 4:

Table 19-5. Probability lists for Table 4.

h t

transition 1 ((h .6)(t .4))

Page 208: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

transition 2 ((h .2)(t .8))

To turn these probability lists into transition lists only requires that we explicitlyassociate a past outcome with each set of probability pairs. We can do this byadding the past outcome to the front of each probability list:

Table 19-6. Transition lists for Table 4.

past h t

transition 1 (h (h .6)(t .4))

transition 2 (t (h .2)(t .8))

Each transition list is now a structure linking past outcomes with a probabilitydistribution for generating the next outcome. The first element in each transitionis the past outcome and the rest of the elements in the list are the probabilitypairs. Note that to implement a second order table each past value would be alist of past values. For example, a second order representation for Table 19-6might look like:

Table 19-7. Second order transition lists.

past h t

transition 1 ((h h)(h .9)(t .1))

transition 2 ((h t)(h .9)(t .1))

transition 3 ((t t)(h .9)(t .1))

transition 4 ((t h)(h .9)(t .1))

Notice that as the order of the Markov process increases so does the length ofthe transition table since each possible combination of past outcomes must beexplicitly represented in the table.

In our implementation of transition tables, each ttable (transition table) willconsist of a list of transition rows. Each transition row will itself be a list thatassociates one or more past outcomes with a ptable (probability table) forgenerating the next outcome (Table 6.) To implement ttables will require onlythree new functions.

1. The constructor make-ttable creates a ttable from an externalspecification of its data.

2. The accessor find-ptable finds the ptable for a specified past outcomein a ttable.

3. The Markov selection function mran returns the next random outcome froma ttable and past outcome.

Our implementation assumes that we have already defined the required ptabledefinitions presented in Example 18-11 of the previous chapter. Thesedefinitions are shown again here for the reader's convenience (Example 19-1).

Page 209: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 19-1. Probability tables functions discussed in

Chapter 18.

(define (make-ptable data) (let ((total (loop for d in data sum (second d))) (sum 0)) ;; total holds sum of weights in data (loop for d in data for v = (first d) ; outcome to return for w = (second d) ; relative weight do (set! sum (+ sum w)) ;; collect outcome and normalized probability collect (list v (/ sum total)))))

(define (pran table) ;; return outcome in table according ;; to its weighted probability (let ((x (random 1.0))) ;; x is uniform number < 1. (loop for d in table for p = (second d) when (< x p ) ; x in this segment. return (first d))))

We start by defining the function make-ttable that creates a transition table forgenerating Markov chains (Example 19-2).

Example 19-2. Defining a transition table constructor.

(define (make-ttable data) ;; create a transition table from data. (loop for trans in data for past = (first trans) for probs = (rest trans) ;; each row transition is a past value and a ptable collect (list past (make-ptable probs))))

The function make-ttable creates a transition table from a list of data. Each

element in data is a transition list trans. The first element in each trans is a pastoutcome and the rest of the data are the probability pairs probs that make upits distribution. The make-ttable returns a transition table (list) as its value.Each element in the table is transition row (list) that associates a past outcomewith a probability table created by make-ptable.

Interaction 19-1. Creating a transition table from Table 4.

cm> (define coin (make-ttable '((h (h .6) (t .4)) (t (h .2) (t .8)))))

cm> (pprint coin)((h ((h 0.6) (t 1.0))) (t ((h 0.2) (t 1.0))))

Page 210: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm>

The next step is to define the function find-ptable which returns a ptable for aspecified past outcome. This function will accept two arguments, the transition

table ttable and the past outcome past, and will return the probability tableassociated with past in the transition table. The function will find the ptable by

iterating over transition rows and matching each row's past outcome with past.If they are the equal then the probability table for that transition is returned. Butwhat does “equal” actually mean? The past outcomes we must match might benumbers, symbols (as is the case in our example table) or even lists of data.How to we test the equivalence of symbols and lists? The Lisp function equal?is a predicate for testing general equivalence between two Lisp objects:

Interaction 19-2. The equal? general equivalence

predicate.

cm> (equal? 1 1)#tcm> (equal? 1 1.0)#fcm> (equal? 'a 'a)#tcm> (equal? '(t t) '(t t))#tcm> (equal? '(h t) '(t h))#fcm>

As a general rule of thumb, two objects are considered equal? if they look thesame, otherwise they are not equal. Given the equal? general equivalencepredicate we can define the probability table lookup function:

Example 19-3. A lookup function to find the ptable for a

specified past outcome.

(define (find-ptable ttable past) ;; find the transition matching past and return its ptable (loop for trans in ttable for this = (first trans) if (equal? this past) return (second trans)))

The function find-ptable returns the probability table in ttable whose past

outcome is equal to past. A loop iterates over each transition row trans in thetable and compares past with the past outcome in the first position of each row.If these outcomes equal? then the loop immediately returns the probability table

that is stored as the second element of the row trans. Note that the case of notfinding a match for a past outcome should not happen. If it does (throughmistaken input by the caller) the function will return false.

Page 211: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 19-3. Looking up ptables for past outcomes.

cm> (find-ptable coin 't)((h 0.2) (t 1.0))cm> (find-ptable coin 'h)((h 0.6) (t 1.0))cm> (find-ptable coin 'xyz)#fcm>

The final step in the implementation is to define the function mran that

implements Markov selection. The function is passed two inputs, a ttable and a

past outcome and returns the next outcome. The outcome is generated bycalling the pran function on the probability table returned by find-ptable. Thepran function was discussed in the previous chapter and its definition isincluded here as well.

Example 19-4. Defining the Markov selection function.

(define (pran table) ;; return outcome in table according ;; to its weighted probability (let ((x (random 1.0))) ;; x is uniform number < 1. (loop for d in table for p = (second d) when (< x p ) ; x in this segment. return (first d))))

(define (mran ttable past) ;; find row matching past and return its ptable (let ((ptab (find-ptable ttable past))) (if ptab (pran ptab) #f)))

We can now use Markov selection to return values from our "coin":

Interaction 19-4. Two methods for Markov selection.

cm> (loop with past = 'h repeat 20 for y = (mran coin past) collect y do (set! past y))(h h t t t h h h h t t t t t h h t t t t)cm> (loop repeat 20 for x = 'h then (mran coin x) collect x)(h h t t t t t h h t t h h t t t h h h t)cm>

Page 212: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 19-4 demonstrates two different strategies for controlling a Markov

selection process. The first loop initializes past to one of the possible outcomesin the table. Each iteration of the loop then generates a value from the transition

table and updates past with that value so that the next time through the loop thatvalue is passed to mran as the past outcome. In the second loop the collectionstarts with a particular value and then updates that value on each subsequentiteration. In addition to being simpler than the first loop, the second method alsoallows the composer to specify a particular initial value for the beginning of theprocess. Other strategies for controlling the starting and ending points ofMarkov processes will be presented in the next few examples.

Musical Examples

We will now use our implementation of transition tables in a several musicalcontexts. For examples of higher order Markov processes please see thediscussion of the Markov pattern in Chapter 20.

Markov Rhythmic Patterns

Imagine generating a melody for a soloist in which rhythmic values aredetermined by random selection. Even if only very simple rhythms are used amoments reflection should tell us that the patterns produced by the process willnot reflect any underlying pattern of beat. Assume that the process is restrictedto quarters, dotted-eights, eighths, and sixteenths (which we notate q, e., e, ands respectively). Since the random process can place sixteenths and dottedeighths anywhere within a beat, the sequence of rhythmic values that result willonly occasionally line up with the start of a metric pulse:

→ uni-1.mid Uniform rhythmic distribution with tempo curveand random walk melody.

In this first Markov example we generate random rhythms that clearly expressan underlying beat and tempo curve. A random walk is used to generate themelody.

Example 19-5. Comparison of uniform and Markovrhythmic patterns.

(define rhy-table '((q (q .5) (e 2) (e. .75) ) (e (e 3) (q 1) ) (e. (s 1)) (s (e 2) (q 1))))

(define tcurve '(0 1 .7 .75 1 1)) ; tempo envelope

(define (markov-rhythms len) ;; rhythms generated from first order Markov

Page 213: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(let ((tbl (make-ttable rhy-table))) (process for i below len for k = 60 then (drunk k 6 :low 40 :high 80 :mode :jump) for r = 'q then (mran tbl r) for d = (* (rhythm r 120) (interpl (/ i len) tcurve)) output (new midi :time (now) :keynum k :duration d) wait d)))

The Markov table rhy-table assigns highest weight to eighth notes and the leastweight to dotted eighths. If a dotted-eighth is played then the next value must bea sixteenth, which forces the pattern to return back to the beginning of the beatagain.

Interaction 19-5. Listening to Markov rhythm patterns.

cm> (events (markov-rhythms 80) "rhy.mid")"rhy-1.mid"cm>

→ rhy-1.mid

The next version of the rhythmic process demonstrates an alternate method forcontrolling the Markov process. The table in Example 19-6 encodes two extratransitions that represent unique states in the process. An initial state start forthe table is represented by the first transition in the table. The output from thistransition can be either a dotted-eighth or a quarter. A stopping state for thetable is represented by the stop outcome of the whole note (w) transition row.Thus if a whole note is selected then the table will be forced to return the uniqueoutcome stop as its next value. A calling process can check for this conditionand terminate iteration when it occurs.

Example 19-6. Markov rhythm with start and stop

transitions.

(define rhy-table2 '((start (e. 2) (q 1)) (q (q .5) (e 2) (e. .75) (w .2)) (e (e 3) (q 1) ) (e. (s 1)) (s (e 2) (q 1)) (w (stop 1))))

(define (markov-rhythms2 ) ;; rhythms generated from first order Markov with ;; explicit start and stop transitions. (let ((tabl (make-ttable rhy-table2)) (past 'start)) (process for k = 60 then (drunk k 6 :low 40 :high 80 :mode :jump) for r = (mran tabl past)

Page 214: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

until (equal? r 'stop) for d = (rhythm r 120) output (new midi :time (now) :keynum k :duration d) wait d set past = r)))

The process markov-rhythms2 checks to see if r is equal to stop, in which casethe process immediately terminates. The disadvantage with this method is thatthere is no way to determine ahead of time how long the process will actuallyrun!

Interaction 19-6. Listening to Markov rhythm patterns.

cm> (events (markov-rhythms2 ) "rhy.mid")"rhy-2.mid"cm>

→ rhy-2.mid

Markov melody

In our next example we use a Markov process to generate a pseudo GregorianChant. The intention is not to generate a stylistically correct chant, but rather tosee how melodic motion can be constrained using a Markov chain and how theprocesses can mimic composition styles. For a much larger example of stylegeneration see the Markov process example in Chapter 20 that implements theStephen Foster example discussed in Computer Music (Dodge)

In the crudest of terms, a Gregorian Chant is characterized by mostly stepwise motion within a range of modal degrees. From any given tone there ismore likelihood that a chant will move a step up or down than then to skip athird or fourth away. The larger the skip, the more unlikely the chant is to makeit. Certain tones, such as the final and tenor, have more influence over themelody than others. In the Dorian mode the tenor A is occasionally decoratedby the B-flat directly above it. This B-flat almost always returns back to the tenortone. In an authentic mode, the final of the mode acts as a kind of “reflectingboundary” that redirects the melody in the opposite direction. We can mimicthese stylistic tendencies using a first order Markov process:

Example 19-7. Dorian Gregorian Chant

(define dchant '((d4 (d4 .1) (e4 .35) (f4 .25) (g4 .1) (a4 .15)) (e4 (d4 .35) (f4 .35) (e4 .1) (g4 .1) (a4 .1)) (f4 (d4 .2) (e4 .2) (f4 .1) (g4 .2) (a4 .12)) (g4 (d4 .2) (e4 .1) (f4 .3) (g4 .1) (a4 .3) (bf4 .2)) (a4 (d4 .1) (e4 .2) (f4 .25) (g4 .3) (a4 .1) (bf4 .3)) (bf4 (a4 1))))

(define (monks1 len chant rhy) (let ((tbl (make-ttable chant)))

Page 215: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(process repeat len for k = 'd4 then (mran tbl k) output (new midi :time (now) :keynum k duration rhy) wait rhy)))

In Example 19-7 the Gregorian Chant is represented by a transition table thatprefers step wise motion over leaps and repeated tones. Certain leaps, forexample to the B-flat , are not allowed at all. B-flat can only be approached andleft from the tenor note. D4 is the final of the mode and acts as a reflectingboundary that moves the melody upward, usually by step but occasionally to themediant or tenor tones as well.

Interaction 19-7. Listening to Markov monks.

cm> (events (monks1 30 dchant .8) "monks.mid")"monks-1.mid"cm>

→ monks-1.mid

In this next example we look at strategies for shaping a Markov process so thatthe results are more musically satisfying. We know, for example, that inGregorian Chant the tenor and final are more likely to be agogically stressedthen other tones. Moreover, the chant should begin and end on the final of themode.

Example 19-8. Markov with conditional checks.

(define (chant-dur tone dur) ;; adjust dur if tone is D, F or A. (let ((doub (* dur 2))) (if (scale= tone 'd4) (odds .7 doub dur) (if (scale= tone 'a4) (odds .5 doub dur) (if (scale= tone 'f4) (odds .25 doub dur) dur)))))

(define (monks2 end chant rhy oct) (let ((tabl (make-ttable chant))) (process for k = 'd4 then (mran tabl k) for dur = (chant-dur k rhy) output (new midi :time (now) :keynum (transpose k oct) :amplitude .8 :duration dur) wait dur until (and (> (now) end) (scale= k 'd4)))))

Page 216: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The monks2 process generates a note k from a first order Markov chant andthen calls chant-dur to determine the rhythmic value for that tone. chant-durtests k to see if it is the final (d), tenor (a) or mediant (f) of the mode. If k is thefinal then there is a 70% chance that its duration will be doubled. Otherwise, ifthe tone is the tenor then there is a 50% chance the tone will be doubled.Otherwise, if the tone is the mediant f4 then there is a 25% chance the durationwill be doubled. The until clause at the end of the monks2 process stopsplaying if the current note is the mode's final and enough notes have been sung.The oct parameter to the process allows us to transpose each monk up anddown in register without having to alter the process definition. We can nowlisten to monks in octaves and in other interval relations.

Interaction 19-8. Listening to Monks2.

cm> (events (list (monks2 24 dchant .8 12) (monks2 24 dchant .8 0) (monks2 24 dchant .8 -12)) "monks.mid")"monks-2.mid"cm> (events (list (monks2 24 dchant .8 -6) (monks2 24 dchant .8 0) (monks2 24 dchant .8 11)) "monks.mid")"monks-3.mid"cm>

→ monks-2.mid→ monks-3.mid

Markov Harmonic Rules

In this next example we use a Markov process to generate the interval relationsfor a series of chords. The same rules will be used to generate the underlyingmelody on which the chords will be transposed.

Example 19-9. Markov interval patterns.

(define int-mix '((1 (3 .4) (4 .4) (6 .1)) (2 (2 .2 ) (3 .4) (4 .4) (6 .1)) (3 (1 .2 )(2 .6 ) (4 .4) ) (4 (2 .2 ) (3 .4) (4 .4) ) (6 (2 .4) (3 .2) (4 .2))))

(define (markov-chorder len int-mix note size ud rhy dur ) (let ((tabl (make-ttable int-mix)) (int 1) (key (keynum note)) (chord #f)) (process repeat len set int = (mran tabl int) set key = (fit (transpose key (odds ud int (- int))) 50 90)

Page 217: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

set chord = (loop with n = key repeat size collect n do (set! int (mran tabl int)) (set! n (transpose n (- int)))) each c in chord output (new midi :time (now) :keynum c duration dur) wait rhy)))

The markov-chorder process uses the interval content defined in int-mix togenerate len number of chords. Each chord has size number of notes. The

starting point for the series of chords is specified by note and ud is a ratiocontrolling the odds of the melody wandering upward or downward where a ud

value greater than .5 prefers ascending melodic motion. The int variable will beused to store the current value from the Markov table and is initialized to 1. Thisvalue is then updated to a new interval from the Markov table at the beginning of

each iteration of the process. The key variable serves as the key number onwhich each chord is based and its value is calculated by transposing the current

value up or down int intervals according to the ud probability factor. The fitfunction insures that this value lies between key numbers 50 and 90. The nextclause sets the value chord to a list of key numbers based on the current value

of key. These key numbers are computed from the current key and the Markovinterval table. The chord loop initializes the variable n to the current value of key

and then iterates size number of times. On each iteration the value of n is firstcollected and then transposed downward by each interval read from the Markovtable. (The transposition is forced downward simply to allow us to hear themelodic tone on which the chord is based more clearly.) The each processclause then applies an output action clause to each element in the chord.

Interaction 19-9. Listening to Markov generated harmony.

cm> (events (markov-chorder 25 int-mix 'g4 6 .6 1.2 1.2 ) "chord.mid")"chord-1.mid"cm>

→ chord-1.mid

Markov Texture

In this last example we use a Markov process to generate a melodic texture thatreminiscent of both the persistent coin toss model in Table 4 and the GregorianChant process discussed earlier in this section. In this example “heads” isrepresented by black keys and “tails” by white keys. Each black or white key isweighted to prefer moving to nearer tones of the same color. The basicweighting strategy for moving from key to key is shown in Table 19-8

Page 218: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Table 19-8. Transition categories and weight spread.

Repeat current key: 0.5

Near-to-far spread same color 2.0-0.5

Near-to-far spread other color 0.2-0.1

Example 19-10. Persistent black and white keys.

(define bw-intervals ;; first line of each transition hold weights ;; for keys of same color. second lines contains ;; weights for moving to other color '((0 (0 .5) (2 2) (4 1.5) (5 1) (7 .5) (9 .5) (11 .5) (1 .2) (3 .1) (6 .1) (8 .1) (10 .1)) (1 (1 .5) (3 2) (6 1.5) (8 1) (10 1) (0 .2) (2 .2) (4 .1) (5 .1) (7 .1) (9 .1) (11 .1)) (2 (0 2) (2 .5) (4 2) (5 1.5) (7 1) (9 .5) (1 .2) (3 .2) (6 .1) (8 .1) (10 .1) (11 .1) ) (3 (1 2) (3 .5) (6 1.5) (8 1) (10 .5) (0 .1) (2 .2) (4 .2) (5 .1) (7 .1) (9 .1) (11 .1)) (4 (0 1.5)(2 2) (4 .5) (5 2) (7 1.5) (9 1) (11 .5) (1 .1) (3 .2) (6 .2) (8 .1) (10 .1)) (5 (0 1) (2 1.5)(4 2) (5 .5) (7 2) (9 1.5) (11 1) (1 .1) (3 .2) (6 .2) (8 .1) (10 .1)) (6 (1 1.5)(3 2) (6 .5) (8 2) (10 1.5) (0 .1) (2 .1) (4 .1) (5 .2) (7 .2) (9 .1) (11 .1)) (7 (0 .5) (2 1) (4 1.5) (5 2) (7 .5) (9 2) (11 1.5) (1 .1) (3 .1) (6 .2) (8 .2) (10 .1)) (8 (1 1) (3 1.5)(6 2) (8 .5) (10 2) (0 .1) (2 .1) (4 .1) (5 .1) (7 .2) (9 .2) (11 .1)) (9 (0 .5) (2 .4) (4 1) (5 1.5) (7 2) (9 .5) (11 2) (1 .1) (3 .1) (6 .1) (8 .2) (10 .2)) (10 (1 .5) (3 1) (6 1.5) (8 2) (10 .5) (0 .1) (2 .1) (4 .1) (5 .1) (7 .1) (9 .2) (11 .2)) (11 (0 .5) (2 .5) (4 .5) (5 1) (7 1.5) (9 2) (11 0.5) (1 0.1) (3 .1) (6 .1) (8 .1) (10 .2))))

(define bw-octaves '((c3 (c3 2) (c4 1) (c5 .5) (c6 .25)) (c4 (c3 1) (c4 2) (c5 1) (c6 .5)) (c5 (c3 .5) (c4 1) (c5 2) (c6 1)) (c6 (c3 .25) (c4 .5) (c5 1) (c6 2))))

(define (bw len oct rate) (let ((ints (make-ttable bw-intervals)) (octs (make-ttable bw-octaves)) (reps 0) (int 0)) (process repeat len if (= reps 0) set reps = (pick 4 8 12 16) and set oct = (mran octs oct) set int = (mran ints int) output (new midi :time (now) :keynum (transpose oct int) :duration (* rate 1.5)) wait rate set reps = (- reps 1))))

Page 219: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The bw process uses two Markov tables to project an interval pattern of blackand white key motion onto a pattern of octave transpositions. The Markov table

octs that controls the octave register “prefers” motion to closer octaves than tooctaves further away, which causes the octave register to generally shift in asmooth manner. The reps variable holds the number of notes remaining to play

in the current octave. If reps is zero then a new octave is selected from octs andreps is reset to a random length of 4, 8, 12, or 16 events. The key number for

each event is determined by transposing the current octave offset oct by aninterval int generated from the Markov interval table ints. The last statement in

the process decrements the value of reps by 1.

Interaction 19-10. Listening to Black and White.

cm> (events (bw 120 'c4 .125) "bw.mid")"bw-1.mid"cm> (events (list (bw 120 'c4 .125) (bw 120 'c3 .125)) "bw.mid" '(0 4))"bw-2.mid"cm> (events (list (bw 120 'c4 .125) (bw (* 120 21/13) 'c4 (* .125 13/21))) "bw.mid")"bw-3.mid"cm>

→ bw-1.mid→ bw-2.mid→ bw-3.mid

The third call in Example 19-10 creates two rhythmic strata related by theGolden Mean ratio 21/13.

Markov Analysis

The weights for all of the preceding Markov processes were determined only bythe preference of the author. But Markov processes can also model thebehavior of real world systems by analyzing the data and extracting a set ofoutcomes and weights that regenerate chains with the same characteristics. Toanalyze the data a “window” is positioned over successive element in thesequence. The width of this window is determined by the order of the analysis.An analysis for a first order table uses a window width of 2: the first elementrepresents a past outcome and the second is its successor. In a second orderanalysis the window is 3 elements wide, the first two elements are the pastoutcomes and the third element is that past's successor. The analysis proceedsiteratively, by moving the window sequentially over the elements and countingthe number of times each unique outcome happens for each unique past. Wecan perform an example analysis on the following short sequence of data:

a a b c b a

Page 220: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

A first order window examines pairs, the first element in the pair represents apast value and the second element is its outcome. Windowing over theexample sequence produces the following analysis pairs:

aa, ab, bc, cb, ba, aa

The very last window in the analysis (aa) is produced by “wrapping” the finalwindow around to the front of the sequence again so that the last element has asuccessor. An analysis that wraps around creates a “seamless” chain withoutany terminating condition. An alternate method of analysis could encodes aunique terminal value, call it z, as the consequent of the last a. When z appearsfrom the chain the caller will knows that the Markov process cannot generateany more events.

The next step in the analysis is to count each unique outcome for eachunique past, giving us the raw counts shown in Table 19-9. Past events arerepresented as row labels and successor events are labeled by columns.

Table 19-9. Raw count tally.

a b c

a 2 1 —

b 1 — 1

c — 1 —

Table 19-11 shows the raw counts normalized to probability proportions.

Table 19-10. Normalized to proportions.

a b c

a 0.667 0.333 —

b 0.500 — 0.500

c — 1.000 —

It should be obvious even from this short example that doing a Markov analysis“by hand” is both tedious and error prone. Luckily for us, it is also iterative innature and so something that computers can easily do!

Analysis By Computer

Common Music provides the function markov-analyze to generate nth orderMarkov analysis of a sequence of data.

(markov-analyze seq [:order i] [:print? b] [:pattern? b] [:key p])

The type of results returned by the function vary according to its :print? and:pattern? keyword arguments. One or more of the follow results are possible:

Page 221: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

1. A printed table of results from the statistical analysis.2. A pattern definition that describes the pattern.3. A pattern object that implements the pattern.

Only the first and third results will be demonstrated in this chapter, see thediscussion of the Markov pattern in Chapter 20 for information about thesecond option.

The following analyses generate different Markov orders from the melodyHappy Birthday. The effect of Markov on Happy Birthday was demonstrated tome by the composer Paul Berg, who works at the Institute of Sonology. Sincehigher orders of analysis generate tables too large to fit on a book page theanalyses shown below include tables for only the first two orders. However, thereader can still generate the other orders interactively in Lisp. Happy Birthday isfully expressed by a fifth-order Markov process. We first define the datasequence to analyze and a process to play the Markov renditions:

Example 19-11. Happy Birthday, data and player.

(define bday (note '(c4 c d c f e c c d c g f c c c5 a4 f e d bf bf a f g f)))

(define (play-bday order reps rate) ;; process to play happy birthday in various orders. (let ((pat (markov-analyze bday :order order :print? false))) (process repeat reps output (new midi :time (now) :keynum (next pat) :duration (* rate 1.5)) wait rate)))

Happy Birthday, Zero-Order Markov (weighted randomselection)

(markov-analyze bday :order 0 :print? :table :pattern? false)

Table 19-11. Zeroth order transition weights.

c4 d4 e4 f4 g4 a4 bf4 c5

0.32 0.12 0.08 0.20 0.08 0.08 0.08 0.04

Interaction 19-11. Listening to zero order Happy Birthday.

cm> (events (play-bday 0 40 .15) "bday.mid")"bday-1.mid"cm>

→ bday-1.mid

Page 222: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Happy Birthday, First Order Markov

(markov-analyze bday :order 1 :print? :table :pattern? false)

Table 19-12. First order transition weights.

c4 d4 e4 f4 g4 a4 bf4 c5

c4 0.38 0.25 — 0.13 0.13 — — 0.13

d4 0.67 — — — — — 0.33 —

e4 0.50 0.50 — — — — — —

f4 0.40 — 0.40 — 0.20 — — —

g4 — — — 1.00 — — — —

a4 — — — 1.00 — — — —

bf4 — — — — — 0.50 0.50 —

c5 — — — — — 1.00 — —

Interaction 19-12. Listening to first order Happy Birthday.

cm> (events (play-bday 1 40 .15) "bday.mid")"bday-2.mid"cm>

→ bday-2.mid

Happy Birthday, Second Order Markov

(markov-analyze bday :order 2 :print? :table :pattern? false)

Table 19-13. Second order transition weights.

c4 d4 e4 f4 g4 a4 bf4 c5

c4 c4 — 0.67 — — — — — 0.33

c4 d4 1.00 — — — — — — —

c4 f4 — — 1.00 — — — — —

c4 g4 — — — 1.00 — — — —

c4 c5 — — — — — 1.00 — —

d4 c4 — — — 0.50 0.50 — — —

d4 bf4 — — — — — — 1.00 —

e4 c4 1.00 — — — — — — —

e4 d4 — — — — — — 1.00 —

f4 c4 1.00 — — — — — — —

f4 e4 0.50 0.50 — — — — — —

f4 g4 — — — 1.00 — — — —

Page 223: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

g4 f4 1.00 — — — — — — —

a4 f4 — — 0.50 — 0.50 — — —

bf4 a4 — — — 1.00 — — — —

bf4 bf4 — — — — — 1.00 — —

c5 a4 — — — 1.00 — — — —

Interaction 19-13. Listening to second order Happy

Birthday.

cm> (events (play-bday 2 40 .15) "bday.mid")"bday-3.mid"cm>

→ bday-3.mid

Happy Birthday, orders 3-5

(markov-analyze bday :order 3 :print? :table :pattern? false)(markov-analyze bday :order 4 :print? :table :pattern? false)(markov-analyze bday :order 5 :print? :table :pattern? false)

Interaction 19-14. Listening to Happy Birthday, orders 3, 4

and 5.

cm> (events (play-bday 3 40 .15) "bday.mid")"bday-3.mid"cm> (events (play-bday 4 40 .15) "bday.mid")"bday-4.mid"cm> (events (play-bday 5 40 .15) "bday.mid")"bday-5.mid"cm>

→ bday-3.mid→ bday-4.mid→ bday-5.mid

Amazing Grace

As a final example of Markov processes I offer a piece I composed from aMarkov analysis of the spiritual Amazing Grace. The piece also uses weightedrandom selection and envelopes, both of which have been covered in Chapters15 and 17. The only real difference between the Markov processes you willhear in the composition and the process described in this chapter is in themanner in which probability weights were controlled in the table. Rather thandevelop multiple tables to represent the folk song I implemented a single largetable whose probability weights were expressed as envelopes. The probabilityin this large Markov table was then controlled as a function of time.

Amazing Grace is an algorithmic fantasy for the Yamaha Disklavier (a MIDI

Page 224: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

controlled piano) based on the American folk song of the same name. Thefantasy involves a processes of becoming, in which melodic and rhythmiccontours of the folk song serve as gravitational centers for the musical texture tocoalesce around, like dust spiraling inward to form a star. Amazing Gracebegins in a mode and texture very distant from the folk melody. As thecomposition unfolds the original tune gradually exerts more and more influenceover the stochastic texture. Short melodic motives and rhythmic figures firstappear and are followed by progressively longer gestures and melodiccontours coming to the fore. The process continues until the point of maximuminfluence, at which time the fantasy has congealed into a block texture with asingle rhythmic motive and melodic content completely determined by the folksong. Amazing Grace was commissioned for the opening ceremony of theMultimedial III Festival in Karlsruhe Germany, where it received its worldpremiere in 1995.

→ Amazing-Grace-KA.mp3 stereo MP3, 8 Mbytes, 7minutes.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file markov.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

References

Dodge, C. & Jerse, T. (1985). Computer Music. New York: Schirmer Books.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 225: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

20Patterns and Composition

Art is the imposing of a pattern onexperience, and our aesthetic enjoyment is

recognition of the pattern.

— Alfred North Whitehead

It is hard to conceive of a musical work that does not express patterns somehow.Patterns can operate at all levels of a composition, from the transitory surface flowof notes and rhythms to its deep structural organization. A pattern might involve asingle sound parameter such as frequency, or it may involve multiple soundattributes evolving in parallel over time. Because patterns permeate musicalexpression, inventing and manipulating them is fundamental to the work that acomposer does. In this chapter we explore an object oriented language formusical pattern description based on a class of object called pattern. Every typeof pattern organizes and generates data according to class specific orderingrules. The pattern language provides a number of different patterns to work with.These patterns can be used individually or combined together to form compositepatterns that simultaneously exhibit the characteristics of several different patterntypes. Table 20-1 contains a summary of the basic pattern classes that will bediscussed in this chapter. Be sure to consult the Common Music Dictionary for fulldetails about each type of pattern.

Table 20-1. Table of pattern classes.

class description

cycle sequences elements in a continual loop.

line sequences elements and sticks on last.

palindromesequences elements forwards and backwards.

heap permutes elements randomly.

random selects elements in weighted distribution.

rotation permutes elements by rotation rules.

markov generates elements by markov process.

graph generates elements by state transition rules.

funcall computes elements by calling a function.

rewrite rewrites elements using grammar rules.

Page 226: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The pattern language also provides some pattern classes that implementspecialized functionality. These specialized patterns (Table 2) are almost alwaysused in conjunction with the general patterns listed in Table 1. Their use will bediscussed at appropriate places over the course of this chapter.

Table 20-2. Table of specialized pattern classes.

class description

join forms a single pattern out of multiple patterns.

copier repeats periods of a pattern.

chord produces lists of elements.

transposertransposes patterns and lists.

range generates numerical ranges.

pval evaluates a Lisp expression inside a pattern.

Before examining the pattern classes in more detail we first describe somecharacteristics that all patterns share and demonstrate how patterns are createdand used.

Pattern Properties

The manner in which a pattern generates data is influenced not only by whatspecific pattern type it is but also by a set of common properties that control theway in which all patterns behave. The period length is one of the most importantproperties that all patterns possess. Patterns group their data into “chunks” calledperiods and the period length of a pattern determines the number of elements that

constitute a chunk's worth of data. All patterns also possess a repeat property thatplaces an optional ceiling on the number of periods, or chunks, that the pattern willbe allowed to produce. If a pattern reaches its repeat factor it will only generate anend of data marker from that point onward. This marker allows a caller todetermine when a pattern is done producing elements without knowing inadvance how many elements a pattern will actually produce. A pattern's nameproperty can be used to “fetch” a pattern repeatedly as a motive, or building-block, inside the definition of a surrounding pattern.

Working with Patterns

Patterns are created using the new macro. All patterns support the followingproperty initializations:

:of dataSets the pattern's elements to data. This initialization can also be specifiedusing the :notes or :keynums aliases,in which case data is parsed as a listof note names or key numbers, respectively.

:name {string | symbol}

Sets the name of the pattern to string or to the print name of symbol.:for {number | pattern}

Page 227: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

Sets the period length of the pattern to number or pattern of numbers.:repeat {number | pattern}

Sets the repetition factor of the pattern to number or pattern of numbers.:counting {:periods | :values}

Determines how the pattern counts subpatterns. The default value is:periods, which means each period of a subpattern is counted as a singlechoice in the pattern.

Example 20-1 demonstrates three simple pattern definitions.

Example 20-1. Pattern creation using the new macro.

(new cycle :of '(a b c))

(new heap :keynums '(c4 b d5 ef3) :for 20 :name 'gesture1)

(new heap :of (list (new cycle :of 'q :for 3) (new palindrome :of '(e. s e)) (new random :of '(h w h.) :for 1)))

The first expression in Example 20-1 creates a cyclic pattern of three symbols: a,b and c. The second expression defines a random permutation of key numberswith twenty key numbers per period. The third expression creates a compositerhythmic pattern built from three different subpatterns.

Reading Pattern Values

Once a pattern has been created its elements are accessed, or read, using thenext function:

(next pattern [amount])

The next function returns the next value, or list of values, from pattern. Theoptional amount parameter determines how many values are read from thepattern. If amount is not specified then just the next element in the pattern isreturned. If amount is an integer then that many values are read and returned in alist. Otherwise, if amount is not false, then a period's worth of elements is readfrom the pattern and returned in a list.

Interaction 20-1. Creating and reading patterns.

cm> (define p (new cycle :of '(a b c)))

cm> (next p)acm> (next p)bcm> (next p)ccm> (next p #t)

Page 228: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(a b c)cm> (next p 7)(a b c a b c a)cm> (next p #t)(b c)cm>

Example 20-1 creates a cyclic pattern and reads elements from it using the nextfunction. A cycle pattern generates its elements in sequential order and thenloops back to its first element after the last value has been produced. Since no:for initialization was specified in Example 20-1 the cycle's period length wasautomatically set to three, the number of elements in the pattern's data. The fourthcall to next specifies true for the amount parameter, which returns one full periodof the pattern's elements in a list. Note that the last expression only returns twovalue in the period because only two values were left in the current period due tothe previous call to next.

Subpatterns

Patterns can contain any type of Lisp data, including other patterns. A subpatterndefines a local ordering of elements within the scope of the surrounding pattern.As a general rule, any value specified to a pattern can be replaced by asubpattern of values. This rule applies not only to pattern data, but to many of thepattern's properties as well. In this way the characteristics of a pattern can changeand evolve along with the data that the pattern generates.

Subpatterns typically produce an entire period of their elements before anyother element in the surrounding pattern's data can be chosen. Normally all ofthese elements count as only one choice in the containing pattern's period count.In contrast, a subpattern specified to a property produces just one new elementeach time the surrounding pattern starts a new period. We will use a simple firstexample to demonstrate some of the basic features of subpatterns.

Figures and Motives

Every composer is familiar with the Alberti Bass, a “broken chord” figure thatappears in many works from the Common Practice period. Example 20-2 showsthe definition of two Alberti figures using the cycle pattern in which the second andfourth elements are the same note.

Example 20-2. Alberti figures implemented as patterns.

(new cycle :notes '(c3 e g e) :name 'm1)

(new cycle :notes '(d3 f g f) :name 'm2)

The figures were given the names m1 and m2 so that they can be usedrepeatedly as motivic units inside a larger pattern. Naming is a convenience thatallows a pattern to be fetched using the #& feature without having to define globalvariables to store each pattern object. In Example 20-3 the m1 and m2 patterns

Page 229: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

are treated as building blocks to form a little bass line in which each subpatternappears several times. Since a cycle continually loops its data from back to front,the Alberti figures in Example 20-2 begin with their first note each time they arerepeated in the surrounding pattern.

Example 20-3. Alberti figures used in a short Alberti bassline.

(new cycle :name 'bass-line :of (list #&m1 #&m1 #&m2 #&m2 #&m1 #&m2 'c3))

→ pat-1.mid

Multidimensional Patterns

The short bass line pattern defined in Example 20-3 generates values for a singlesound parameter. But musical motives and gestures are often defined acrossmore than one dimension of sound. For example, the opening motive ofBeethoven's Fifth symphony is as much a rhythmic contour as it is a pitch contour.In simple cases multiple sound parameters can be managed using separate,independent patterns that control the parameters in parallel. However, thisparallelism ignores the underlying unity of the motive and may become confusingas the size and complexity of the motivic patterns increases. Worse yet, if themotivic patterns are not completely deterministic then the two independent,parallel patterns will not generate values in the required synchronized order.

Common Music's pattern language provides a special type of pattern calledjoin which can be used to link more than one pattern together to form anaggregate unit of structure that will always produce synchronized valuesregardless of how the joined patterns are organized inside a larger pattern.Example 20-4 rewrites the bass line from the preceding example using joins toform two-dimensional musical motives with both pitch and rhythm linked together.

Example 20-4. Frequency and rhythm joined together.

(new cycle :of (list (new join :name 'j1 :of (list #&m1 (new cycle :of '(te ts te ts)))) #&j1 (new join :name 'j2 :of (list #&m2 's)) #&j2 #&j1 #&j2 '(c3 h)))

→ pat-2.mid

In the first join every note from m1 combines with a corresponding triplet rhythmfrom a cyclic rhythm pattern. The second join links each note in m2 with asixteenth note value. The very last element in the pattern (c3 h) defines a half-note C3 value.

Joined motives with random characteristics will still maintain their conceptualintegrity as single units of structure. Example 20-5 demonstrates a pattern that

Page 230: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

joins frequency, rhythm and amplitude values to form gestures involving threedimensions of sound. The overall pattern is a cycle of three local gestures, thesecond and third gesture produce random variations each time they aregenerated from the larger pattern.

Example 20-5. Three dimensional motives with randomvariation.

(new cycle :of (list (new join :of (list (new cycle :of '(ef5 e4 r)) (new cycle :of '(s s q) ) 'ff)) (new join :of (list (new heap :of '(g4 fs4 fn5 b5)) (new cycle :of (list (new heap :of '(e q e)) 'h)) 'p)) (new join :of (list (new cycle :of '(af5 f5 a4 cs4 c5)) (new heap :of '(e s q) :for 5) (new cycle :of '(pp p mp mf f))))))

→ pat-3.mid

Composite Patterns

Composite patterns are patterns that exhibit the characteristics of more than onetype of pattern at the same time. Example 20-6 presents a simple example ofcombining cyclic and random behaviors together. The outer cycle patternsequences three heap subpatterns. The heaps in Example 20-6 collectivelypartition a single octave into three groups of chromatic notes, where each grouplies a major third above the previous group. A heap pattern behaves like a deck ofcards: it shuffles its elements each time it starts over. Since the heaps in Example20-6 randomly permuted their elements the series of values that results from thecomposite pattern will exhibit localized random orderings within a larger periodicascent.

Example 20-6. Cycles of random permutation.

(new cycle :of (list (new heap :notes '(c3 cs d ef)) (new heap :notes '(e3 f fs g)) (new heap :notes '(gs3 a as b))))

→ pat-4.mid

If the pattern in Example 20-6 is turned “inside out” then randomly ordered cycleswill result:

Example 20-7. Randomly permuted cycles.

Page 231: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(new heap :of (list (new cycle :notes '(c3 cs d ef)) (new cycle :notes '(e3 f fs g)) (new cycle :notes '(gs3 a as b))))

→ pat-5.mid

Pattern Periods

The period length of a subpattern has a large impact on how the overall patternevolves. If, as in the preceding examples, the period length is the same as thenumber of elements then all the elements of a subpattern will be generated beforeany other data in the surrounding pattern will be considered. This sort of traversalis called depth first ordering. By setting the period length of a subpattern to lessthen the number of its elements, the subpattern will yield to other patterns in thesurrounding data and produce breadth first ordering.

We can observe the effect of a breadth-first traversal by setting the periodlengths of the of inner heaps in Example 20-6 to 1. A period length of 1 will causeeach heap to produce only 1 note per period of the outer cycle. This will generatea different four notes from the three heaps each period of the outer cycle, thusturning the pattern into a “random generator” of Alberti-like figures.

Example 20-8. Randomized Alberti figures.

(new cycle :of (list (new heap :notes '(c3 cs d ef) :for 1) (new heap :notes '(e3 f fs g) :for 1 :name 'm) (new heap :notes '(gs3 a as b) :for 1) #&m))

→ pat-6.mid

Note that the fourth element #&m in the pattern does not produce a repeatedvalue in the melodic output because #&m is a repeated pattern, not a repeatedvalue. To make our generator repeat the exact same value for the second andfourth element will require that the fourth element literally reference whatever valuethe middle pattern (named m) produced. The function pattern-value can beused to return the last value that a pattern generated:

Interaction 20-2. Referencing the last value a patternproduced.

cm> (define p (new cycle :of '(a b c)))

cm> (pattern-value p):not-a-datumcm> (next p)acm> (pattern-value p)acm>

Page 232: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Macro]

The first call to pattern-value returned the special keyword token :not-a-datumbecause the pattern had no value at that point. Once next reads a value thenpattern-value can fetch that value again from the pattern that produced it.

Note that in the case of our random Alberti generator the pattern-valueexpression will have to be evaluated inside the pattern, every time the fourthelement is to be read. This means that the expression (pattern-value #&m)must appear in the list of data specified to the pattern when it is created. But thispresents problem: the list of data specified to the new macro is evaluated whenthe pattern is created, not when the pattern is read. This means our call to pattern-value must somehow be delayed until the pattern is actually read using next. Aspecial pattern macro called pval (pattern value) can be used to delay theevaluation of a Lisp expression until it is encountered by the next function in apattern.

(pval expr)

Pval delays the evaluation of a Lisp expression expr until the pval is encounteredin a pattern by next. Pval allows any Lisp expression to be placed and evaluatedin a pattern. A pval expression will be re-evaluated every time it is encountered bynext in the pattern.

Example 20-9. Using pval to delay evaluation of anexpression.

(new cycle :of (list (new heap :notes '(c3 cs d ef) :for 1) (new heap :notes '(e3 f fs g) :for 1 :name 'm) (new heap :notes '(gs3 a as b) :for 1) (pval (pattern-value #&m))))

→ pat-7.mid

The pval in Example 20-9 delays the evaluation of the call to pattern-value untilthe pattern is read using the next function. When the fourth element is accessedthe pval returns the value of its expression in its place. This expression will be re-evaluated each time the fourth element is accessed, effectively repeatingwhatever value was previously produced by the #&m subpattern.

As a final variation on the Alberti bass idea we will place our randomizedAlberti generator inside a copier pattern. A copier is a specialized pattern thatgenerates a period from a source pattern and then repeats, or copies, that periodsome number of times before generating the next new period of data from thesource. The period length of the copier is set to a heap that produces either two,three or five iterations of each period of the random figure.

Example 20-10. Using a copier to repeat pattern periods.

(new copier :of (new cycle :of (list (new heap :notes '(c3 cs d ef) :for 1) (new heap :notes '(e3 f fs g) :for 1 :name 'm) (new heap :notes '(gs3 a as b) :for 1) (pval (pattern-value #&m))))

Page 233: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:for (new heap :of '(2 3 5)))

→ pat-8.mid

Zero-length Periods

A subpattern that sets its period length to zero "disappears" from the surroundingpattern until it is reselected with a period length greater than zero. In the followingexample, the note B4 will periodically appear three, two and then zero times in thesurrounding pattern.

Example 20-11. Subpatterns with zero period length areskipped over.

(new cycle :of (c4 ,(new cycle :of 'ef4 :for (new cycle :of '(1 2))) ,(new cycle :of 'b4 :for (new cycle :of '(3 2 0))) d4))

→ pat-9.mid

Example 20-11 also demonstrates a very useful Lisp feature called thebackquoted list. The pattern's data list contains two note names (C4 and D4) thatare tokens for the pattern to generate and two expressions that, when evaluated,create subpatterns. Recall from Chapter 5 that a backquoted list acts just like aquoted list except that expressions preceded by a comma “,” are explicitlyevaluated. In the case of pattern data, the backquote is useful for notating datalists in which some elements are pattern tokens while others elements areexpressions that create subpatterns.

Patterns and Processes

Patterns can greatly simply the overall design and implementation of a musicalprocess. For example, most of the code in the first processes definition inChapter 14 (Piano Phase) was specifically related to treating lists in a cyclicmanner. Recasting the process to use a cycle pattern to hold the phasing tropeeliminates the need for several local variables and makes the code morecompact and easier to understand.

Example 20-12. Process from Chapter 14 converted to use apattern.

(define (piano1 trope amp chan) (let* ((cycl (new cycle :keynums trope :repeat (length trope))) (rate (rhythm->seconds pp-pulse pp-tempo))) (process for k = (next cycl) until (eod? k)

Page 234: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function][Function][Function][Function]

output (new midi :time (now) :keynum k :duration (* rate 1.5) :amplitude amp :channel chan) wait rate)))

Example 20-12 demonstrates the basic manner in which patterns areincorporated into a process definition. Data for the pattern is usually passed tothe process as a list and the process creates a local pattern object to hold thedata. Each iteration of the process then sets a a for stepping variable tosuccessive key numbers from the pattern. In Example 20-12 the stepping value kthen becomes the keynum value of each MIDI object output to the score.

Creating patterns inside a process is almost always better than definingpatterns globally in the Lisp environment. There are several reasons for this. First,a pattern created inside a process is guaranteed to start from its initial conditionseach time the process function is called. In contrast, a pattern that is definedglobally will resume from the point it last stopped unless the composerremembers to redefine the global pattern before it is used again. Second, if thecomposer uses a local process variable to hold the pattern then no other processcan share that exact same pattern object. If a single pattern is read by multipleprocesses then each process reads alternate values from the pattern rather thansuccessive values. Sharing a pattern between processes can be useful but inmost cases it is probably not what the composer intends.

Pattern State

The current state of a pattern can be just as important to a process as the datathat the pattern produces. To illustrate, the :repeat initialization in Example 20-12established a limit on the number of periods the cyclic trope produced. A processcan use this information to determine when to stop generating events rather thanby keeping count of its iterations. The pattern facility provides several functionsthat allow pattern states to be "inspected" and tested for certain conditions.

(eop? pattern)(eod? pattern)(pattern-value pattern)(pattern-period-length pattern)

The eop? (end of period) function returns true if pattern is currently at the end of aperiod, and the eod? (end of data) predicate returns true if a pattern or a patternvalue has reached an established :repeat limit. The pattern-value functionreturns the previous value read from a pattern (if any) and pattern-period-length returns the current period length of the pattern if one has beenestablished. Example 20-13 demonstrates how these functions might be used tocontrol the actions of a process.

Example 20-13. Using pattern states to control a process.

(define (play-pat reps rate dur amp ) (let ((pat (new heap

Page 235: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:notes '(c4 d ef f g af bf c5) :for (new random :of '(8 12 16)) :repeat reps)) (x 0) (r #t )) (process for k = (next pat) until (eod? k) for p = (pattern-period-length pat) if r set x = 0 output (new midi :time (now) :keynum k :amplitude (interp x 0 .4 (- p 1) amp) :duration dur) wait rate set r = (eop? pat) set x = (+ x 1))))

The play-pat process creates a heap pattern to generate reps periods of notes.Since the heap was given a random period length the process cannot know inadvance how many events it will generate. A test using the eod? function allowsthe process to determine that the pattern's end-of-data marker has appearedonce all of the events in reps periods have been produced. The until clause isused to kill the process as soon as that point has been reached.

The process in Example 20-13 produces a crescendo over every period in theheap pattern without knowing in advance how long each period actually is. Theamplitudes of events within a single period are calculated using linearinterpolation (Chapter 16 and range from .4 to the maximum amp value specifiedto the process. The variable x provides the interpolation lookup value, and rangesfrom 0 to one less than p, the heap's current period length as determined by thepattern-period-length function. The variable r is a flag that marks the beginningof each new period. When r is true then the x lookup variable is reset to 0 to begina new crescendo. The variable r is updated at the end of each iteration by theeop? function, which returns true only if the pattern is at the end of its currentperiod. Note that eod? testing will typically occur at the beginning of a processdefinition, before dependent operations actually reference pattern values, andeop? testing will usually occur at the end of the process definition, after a valuehas been read and processed, because that value may have been the last valuein the current period.

Interaction 20-3. Listening to a pattern.

cm> (events (play-pat 10 .1 .3 .8) "pat-10.mid")"pat-10.mid"cm>

→ pat-10.mid

Sharing Patterns Between Processes

If multiple processes share pattern data then each process can output its ownstream of musical events in which some sound properties depend on a common

Page 236: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

shared “idea” while others remain local to each process definition. One simpleway to implement a shared idea is to treat one of the processes as a “master”that creates and manages the shared pattern and to treat the other processes as“slaves” that only reference it. Care must be taken to insure that the masterprocess executes ahead of the slave processes so that the shared data is readybefore the slaves start using it. This can be done either by scheduling the masterprocess slightly ahead of the slaves, or by insuring that the master process is thefirst process in the list of processes passed to the events function.

Example 20-14 defines two processes that harmonize a random bass lineusing a shared pattern value the-note. The first process is the “master” process, itreads a value from its random bass line pattern, outputs a midi event to play thenote, and sets the shared variable the-note to that pattern value. The secondprocess is a “slave” process that generates a chord on whatever bass note themaster process has last produced. The slave process sets c to a randomlyselected chord from a pattern of chord templates and then transposes each chordtwo octaves above the current value of the-note. The slave's each clause is usedto iterate over each value in the current chord list to output a midi event.

Example 20-14. Sharing pattern values between processes.

(define (two-hands end tempo) (let ((the-note #f)) ; shared pattern value (list ;; master process creates the bass-line and sets ;; the shared variable. (let ((bass (new random :keynums '(e1 f g a b c2 d))) (rhys (new random :of '( h.. h e)))) (process while (< (now) end) for k = (next bass) for r = (rhythm (next rhys) tempo) set the-note = k output (new midi :time (now) :keynum k :duration r :amplitude .8) wait r)) ;; slave procees adds random harmony two ;; octaves above the-note (let ((rhys (new random :of '((q :max 3) (e :max 2) (s :max 1) (t :weight .5 :max 1)))) (harm (new heap :of '((0 3 7 8) (1 5 8 10) (0 3 7 10) (1 5 8 12) (1 3 7 10) (3 5 8 12) (1 5 7 10))))) (process while (< (now) end) for c = (transpose (next harm) (+ the-note 24)) for r = (rhythm (next rhys) tempo) each k in c output (new midi :time (now) :keynum k :duration (* r 2) :amplitude .4) wait r)))))

→ pat-11.mid

Page 237: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Pattern Classes

Most of the patterns we have seen so far have very simple control features andare relatively straightforward to use. But note that the slave process in Example20-14 contains the definition of a random pattern in which each element has beenspecified as a list containing the pattern element followed by keyword nodeproperties like :max and :weight. In this section we examine pattern classes that,like the random pattern, involve special formats in their data specification orcontrol properties.

Palindrome

A palindrome pattern enumerates its data forwards and backwards. Subpatternsare visited in palindromic order but will continue to yield their own internalorderings within the palindrome. The palindrome's :elide initialization controlshow the beginning and ending elements in the pattern data are treated when thepalindrome reverses direction. Table 1 demonstrates the effect of the all thepossible :elide values given example data of four letters: A B C D.

Table 20-3. Effects of :elide on palindrome data.

elide value result

false A B C D D C B A | A B ...

true A B C D C B | A B ...

:first A B C D D C B | A B ...

:last A B C D C B A | A B ...

Example 20-15. A palindrome with subpattern.

(new palindrome :notes (, (new heap :notes '(c4 b3 bf a af g) :for 1) d4 ef f gf) :elide (new random :of '(:first #f)))

→ pat-12.mid

The palindrome in Example 20-15 contains 5 elements, the first of which is aheap subpattern with a period length of 1. The :elide value of the palindrome isset to choose between :first, which causes the heap pattern to not repeat asthe last element in the palindrome, and false, which causes it to repeat.

Rotation

A rotation pattern permutes its elements according to rules specified in the:rotations initialization. The value of :rotations should be a rotation list or apattern that generates rotation lists. A rotation list contains (up to) four numbersthat describe which elements in the pattern will be “swapped”, or exchanged, to

Page 238: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

produce the next rotation. This swapping is applied iteratively over all the data toproduce the rotation for the next period. The format of a rotations list is:

(start step width end)

where start is the initial index (zero based) in the data to begin swapping, step isthe distance to move in the data after each swap, width is the distance betweenswapped elements, and end marks the index in data to stop element swapping. Ifthe :rotations initialization is not provided it defaults to (0 1 1 end), whichcauses the front element to shift to the back on each new period. For example, if ab c d represents the initial order for a rotation, then the default rule (0 1 1)iterates over all elements and swaps adjacent pairs, thus moving the first elementa to its next position at the end of the list:

Table 20-4. Rotation of front element A to back.

initial a b c d

swap first pair b a c d

swap second pair b c a d

swap last pair b c d a

The rotation in Example 20-16 consists of five elements, the first of which is asubpattern.

Example 20-16. The rotation pattern.

(new rotation :notes (, (new range :by -2 :initially (keynum 'c6)) b3 f4 g5 ef6) :rotations (new cycle :of '((0 1 1) (0 1 2))))

→ pat-13.mid

The first element in the rotation is a specialized pattern called a range that cangenerate either bounded or unbounded number ranges. The range used inExample 20-16 returns a series of descending key numbers a whole step apartstarting from key number 84 (C6). The :rotations for the pattern is expressed asa cycle of of two rules so that the pattern alternates rules on each period of thepattern. The first rule is the default rotation (0 1 1) and the second swapselements two steps apart, i.e. elements separated by one element between them.

The example file cring.cm contains some examples of rotations thatimplement change ringing patterns, complex rotational schemes developed byEnglish church bell ringers for generating long sequences of peals without directrepetition.

Random

The random pattern generates elements in a discrete weighted distribution. Bydefault, each element in a random pattern has an equal probability of selection

Page 239: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

and may be repeated in direct succession any number of times. This defaultbehavior can be modified by specifying the element as a node, a list containingthe element followed by a set of optional keyword constraints to place on theelement:

(x [:weight n] [:min i] [:max i])

X is the pattern element, :weight sets the probability of the node relative to otherelements in the pattern, :min sets a lower bound on how many direct repetitionsof the node element must be made before a new element might be selected, and:max sets a ceiling on how many direct repetitions of the node element may bemade before a new element must be selected. An element's :weight value maybe specified as a pattern of weights, in which case a new weight will be selectedfor each period of the random pattern. Note that the :min and :max node valueswill affect the statistical accuracy of the node's probability weight. The process inExample 20-17 uses the random pattern to generate transposition offsets,rhythmic patterns, and amplitude values.

Example 20-17. Random 12-tone generator.

(define (ranrow end) (let ((rows (new transposer :of (new cycle :of '(0 1 6 -5 10 11 5 16 15 21 26 20)) :on (new random :keynums '(c4 cs3 b3 fs4 fs3)))) (rhys (new random :of ((t :min 4) (s :min 2) (q :weight 3) (te :min 3) (tq :min 3) ,(new cycle :of '(e. s)) (e :weight 2)))) (amps (new random :of '((mf :weight 7) (mp :weight 3) p)))) (process while (< (now #t) end) for k = (next rows) for r = (rhythm (next rhys) 70) for a = (amplitude (next amps)) output (new midi :time (now) :keynum k :amplitude a :duration r) wait r )))

→ pat-14.mid

In this example the twelve-tone row is specified as a cycle pattern that istransposed to key numbers selected from a random pattern. (The intervals in therow are not notated within a single octave to provide a slight contour to themelodic line.) A transposer is a specialized pattern that can transpose eitherpatterns or lists of key numbers or notes. The rhythmic values are also selected atrandom; the :min feature is used to insure that rhythmic values of less than aneighth note duration are reselected enough times so as to place the next differentrhythm at the beginning of an eighth note pulse.

Markov

Page 240: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The markov pattern implements nth order Markov chains. The data specified tothe pattern is a list of transition rules where each rule is a list of the form:

(past... -> next ...)

where past is zero or more identifiers that define the transition trigger of the ruleand values to the right of the marker -> define the transition's outcomes. Thenumber of ids the left-hand side contains determine the Markov order of thepattern and their left to right order define the succession from most distant past tomost recent choices. The special id * is a “wild card” that can appear in the lefthand side to match any past id at that position. This allows higher order tables to"collapse" transitions with identical outcomes and near-identical left-hand sidesinto a single transition rule.

By default each transition outcome has the same probability of beingselected. To alter the weight of an particular outcome relative to the otheroutcomes, specify its id in a list together with its weight. For example thetransition rule:

(c -> a (b 2) c)

defines a 1st order Markov transition whose outcome b has twice the selectionprobability than either the a or c outcomes. The transition rule:

(q * a -> e r (d 3) g)

describes a third-order transition triggered whenever a is the most recent pastoutcome and q is the third past outcome. Example 20-18 shows a second ordermarkov pattern that generates rhythms with an underlying pulse. Using thewildcard token * reduces the number of transition rules that would otherwise haveto be entered.

Example 20-18. A second order Markov rhythm pattern.

(new markov :of '((e s -> s) (q s -> e.) (q. s -> s) (h s -> e.) (e e -> s e e. q q. q.. h) (* e -> e) (* s -> e e. q q. q.. h) (* h -> e e. q q. q.. h) (* q.. -> s) (* q. -> e s) (* q -> e e. q q. q.. h) (* e. -> s)) :for 20 :past '(q q))

The optional :past initialization for markov patterns initializes the pattern'smemory buffer with an explicit starting condition for the process. The value (q q)will cause the second to last transition rule in the pattern to be triggered the firsttime the pattern is read.

Page 241: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

→ pat-15.mid Drum Track (Ride Cymbal)

The example files foster.cm and foster-1.mid contain a large example of a secondorder Markov process that generates melodic material based on the analysis ofStephen Foster's folk songs described in chapter eight of Computer Music(Dodge) by Charles Dodge and Thomas Jerse.

Graph

A graph pattern generates elements from transition rules associated with theelements in the pattern. Elements and their transitions are represented as graphnodes. A graph node is a list in the form:

(x [:id n] [:to ids])

where x is the element in the graph node, :id sets the node's identifier, or name,and :to is its transition rule. The transition rule determines the next node to selectif the current node is selected. The value of :to can be a node identifier or patternof identifiers. Note also that if the :to value of each node is a random pattern thenthe graph will implement a first order Markov process. The graph in Example 20-19 generates a melodic line using both random and cyclic transition rules. Thegraph contains 5 elements. Each element is a graph node with an identifier thatuniquely “names” that element in the graph and a transition rule that produces thename of the next node if that node is selected. In Example 20-19 the transitionsare subpatterns that produce the identifiers of the successor nodes. The first fournodes in the graph select their successor nodes by randomly choosing betweentwo possible successors (first order Markov). The fifth node selects its successorby cycling through the first four nodes. Note that the pattern element of the fifthnode is itself a subpattern (heap) that produces a four note melodic fragmenteach time it is selected. The entire graph is embedded in a cyclic pattern thatgenerates one period of the graph followed by a rest.

Example 20-19. A quasi-markov graph

(new cycle :of (,(new graph :of ((c4 :id 1 :to ,(new random :of '(2 5))) (d4 :id 2 :to ,(new random :of '(1 3))) (ef4 :id 3 :to ,(new random :of '(2 4))) (f4 :id 4 :to ,(new random :of '(3 5))) (,(new heap :of '(g4 fs4 b4 c5)) :id 5 :to ,(new cycle :of '(1 2 3 4))))) r))

→ pat-16.mid

Funcall

The funcall pattern implements patterns by function call. Each time a funcall

Page 242: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

pattern requires a new period of elements it calls a user specified function toreturn a list of value that becomes the next period of values in the pattern. Thebest way to work with the funcall pattern is to define functions that create andreturn them. That way the function can be called with different arguments to createdifferent versions of the funcall pattern, as shown in Example 20-20.

Example 20-20. Implementing a pattern with the funcallpattern.

(define (noreps low n) (define (thunk ) (loop repeat n for x = (between hi low x) collect x)) (new funcall :of #'thunk))

The noreps function creates a funcall pattern to return n random values per periodbetween a low and high bounds without any direct repetition. noreps defines ahelper function, called thunk, to do most of its work. The term thunk is used byLisp programmers to describe a function that is called without any arguments.The thunk for noreps uses between to generate a list of random values without anydirect repetitions. This thunk is then passed to the funcall pattern as the function tocall whenever the pattern needs a new period of values. Interaction 20-4demonstrates some sample output from a noreps pattern.

Interaction 20-4. Defining a noreps pattern.

cm> (define p (noreps 60 72 5))

cm> (next p t)(63 61 64 62 70)cm> (next p t)(66 61 67 69 61)cm>

We can use noreps in conjunction with the copier pattern to create a wanderingmelody in which locally random figures are repeated to produce larger scaleperiodicity.

Example 20-21. Repetition and randomness.

(define (wander tmpo) (let ((bass (new copier :of (noreps 41 60 5) :for (new random :of '(1 3 5)) :repeat 10)) (amps (new heap :of '(.6 .4 .4 .4 .4)))) (process for k = (next bass) for a = (next amps) until (eod? k) output (new midi :time (now) :keynum k

Page 243: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:duration (if (> a .5) 5 3) :amplitude a) wait (rhythm 1/20 tmpo))))

The wander process creates a melody that selects different random fragmentsfrom a range and then plays each fragments one, three or five time beforeselecting a new one. The amplitude pattern for the process accents exactly onenote during each fragment so that an irregularly spaced melodic line is “broughtout” from the steady background of notes in each fragment.

Interaction 20-5. Listening to wander.

cm> (events (wander 48) "pat-17.mid")"pat-17.mid"cm>

→ pat-17.mid One channel, monotimbral.

In this next example we use funcall to implement three new patterns. The revpatfunction creates a funcall that reverses (retrogrades) a source pattern. The invpatpattern will invert a pattern and mirror will produce a strict palindrome of apattern.

Example 20-22. Retrograding and inverting patterns.

(define (revpat pat) ;; return the reverse of pat (define (thunk ) (let ((vals (next pat true))) (reverse vals))) (new funcall :of #'thunk))

(define (invpat pat) ;; return the inversion of pat, ;; which must contain keynums or notes. (define (thunk ) (let ((vals (next pat true))) (invert vals))) (new funcall :of #'thunk))

(define (mirror pat) ;; return a period of pat followed ;; by its strict retrograde (define (thunk ) (let ((vals (next pat true))) (append vals (reverse vals)))) (new funcall :of #'thunk))

Note, that the retrograding patterns can be applied to any pattern, regardless ofhow that pattern generates values.

Page 244: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Interaction 20-6. Palindrome of random values.

cm> (define p (mirror (noreps 0 12 6)))

cm> (next p t)(0 2 7 0 2 7 7 2 0 7 2 0)cm> (next p t)(4 3 5 10 0 4 4 0 10 5 3 4)cm>

The thunks for revpat, invpat and mirror all function in a similar manner. Eachthunk sets a local variable vals to the next period of values from the sourcepattern pat and then performs the appropriate operation on that list of data:revpat reverses the list, invpat inverts the list (which must therefore containeither key numbers or note names), and mirror appends the list of values to thereverse of that list. The process in Example 20-23 uses funcall patterns togenerate randomly shuffled prime, inversion, retrograde and retrograde-inversionforms of a randomly generated 12-tone row.

Example 20-23. Random rows with retrograde and inversion.

(define (ranrow2 end offset tmpo chan) ;; get a row, any row. (let* ((arow (shuffle (loop for i below 12 collect i)) ) (main (new cycle :of arow)) ; prime form (rows (new transposer ;; heap of p, i, r and ri forms. :of (new heap :of (list main (invpat main) (revpat main) (revpat (invpat main)))) :on (new cycle :keynums (transpose arow offset)))) (rhys (mirror (new random :of (list (new cycle :of 't :for 4) (new cycle :of 's :for 2) '(q :weight 3) (new cycle :of 'te :for 3) (new cycle of 'tq :for 3) (new cycle :of '(e. s)) '(e :weight 2))))) (amps (new random :of '((mf :weight 7) (mp :weight 3) p)))) (process while (< (now #t) end) for k = (next rows) for r = (rhythm (next rhys) tmpo) for a = (amplitude (next amps)) output (new midi :time (now) :keynum k :amplitude a :duration r :channel chan)

Page 245: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

wait r )))

Each time the ranrow process is called it will create a new 12-tone row and useinvpat and revpat to produce the inversion, retrograde and retrograde inversionforms. The rows variable contains a heap pattern that randomly permutes thedifferent row forms. Each new row is also used to determine its transpositionlevels from the key number offset specified to the process. The rhys variableholds a mirror pattern that produces rhythmic palindromes in which the first half ofeach period contains a randomly generated rhythmic structure and the secondhalf reverses it. In Interaction 20-7 we listen to four versions of the process, eachon a separate channel, where each voice enters 8 seconds later than the last oneand all voices stop a the same time.

Interaction 20-7. Random 12-tone music.

cm> (events (loop for i below 4 collect (ranrow2 32 (transpose 'c3 (* i 12)) 40 i)) "pat-18.mid" (loop for i from 0 by 8 to 16 collect i))"pat-18.mid"cm>

→ pat-18.mid Four channels.

Rewrite

The rewrite pattern produces successive generations of elements, where eachgeneration is created by rewriting the previous generation in term of a grammardefined by the user. A grammar is a set of rewrite rules, a set of instructions for“respelling” the elements in successive generations. For example, consider arewrite pattern whose first generation of data consists of two tokens a and b andwhose grammar consists of the following two rewrite rules:

rule 1: a -> b arule 2: b -> a a b

The first rule says that every A in the current generation should be replaced(rewritten) with the sequence B A. The second rule says to replace every B in thecurrent generation with the sequence A A B. If the initial generation in the rewritepattern is set to A B then the rules will produce the following pattern generations:

Table 20-5. Rewrite transformations from rules.

generation pattern elements

Page 246: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

1 a b

2 b a a a b

3 a a b b a b a b a a a b

4b a b a a a b a a b b a a a b b a

a a b b a b a b a a a b

Generation 2 was created by applying the grammar rules to generation 1. Rule 1replaced every a in generation 1 with b a and rule 2 replaced every b with b a a.This transformed the initial generation a b into the second generation's sequenceb a a a b. Successive generations are produced in a similar manner. Therewrite rules defined above are additive, which means that successivegenerations become longer and longer. Rewrite sequences can become verylong. The rewrite pattern's :generations initialization can be used to control themaximum number of generations that the pattern will produce. If a rewrite patternreaches this optional limit it will simply cycle over the last generation from thatpoint onward.

The rewrite pattern supports two different styles of rule specification. Contextfree rules are rules that are associated with specific elements in the pattern.Context sensitive rules are rules that are applied to a set of elements at the sametime.

Context Free Grammars

In a context free rewrite pattern each element of data is represented by a node inthe pattern. The node associates a single element with a rule to replace thatelement in the next generation. This type of rule is termed “context free” becausethe rule depends only on the node itself and not its context, or position, in thesurrounding nodes in the pattern. The form of a context free node is almostidentical to the graph pattern:

({element} [:id x] [:to {id | (id ...) | pattern | false})

The only difference is that the value of :to can be an id, a list of ids, a pattern thatproduces ids, or false. Interaction 20-8 shows the rules discussed in thepreceding section implemented as a context-free rewrite pattern:

Interaction 20-8. Three generations from a context-freepattern.

cm> (define p (new rewrite :of '((a :to (b a)) (b :to (a a b))) :initially '(a b)))

cm> (next p 19)(a b b a a a b a a b b a b a b a a a b)cm>

Example 20-24 shows a rewrite pattern similar to Example 20-23 that generatesa melodic line. The pattern contains two tokens +2 and -2 that are treated as

Page 247: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

increments between the notes of a melodic line:

Example 20-24. Rewrite melody.

(define (stepper end rate from) (let ((pat (new range :initially (keynum from) :stepping (new rewrite :of '((+2 :to (+2 -2 +2)) (-2 :to (-2 -2 +2))))))) (process while (< (now) end) output (new midi :time (now) :keynum (next pat) :duration rate) wait rate)))

Example 20-24 uses the same grammar that the composer Tom Johnson usedhis piece Formulas for String Quartet. Given an alphabet of +2 and -2 the patternrewrites each +2 with the sequence +2 -2 +2 and -2 with -2 -2 +2. In our examplewe interpret +2 or -2 as whole step up or down. The range pattern in the exampledescribes a “walk” that starts with a particular key number from and proceedsaccording to the steps returned by the rewrite pattern. The range patterninitialization of the offset for the walk just one time, and the :stepping propertycauses the range's increment to be read in parallel with the offset. We listen tofour simultaneous versions of the stepper process, each with a different rhythmand pitch offset.

Interaction 20-9. Listening to four voice stepper.

cm> (events (list (stepper 20 (rhythm 's 100) 'c5) (stepper 20 (rhythm 'te 100) 'f4) (stepper 20 (rhythm 'e 100) 'bf3) (stepper 20 (rhythm 'tq 100) 'ef3)) "pat-19.mid")"pat-19.mid"cm>

→ pat-19.mid Four channels.

Context Sensitive Rules

Context sensitive rules can apply to any number of rewrite terms. Since there canbe more or fewer rules than the terms they act upon, a context sensitive patternseparates the nodes of the pattern from the rules that act on them. In a contextsensitive rewrite pattern, each node in the current generation is matched againstthe entire rule set to find successor rules to apply to the node. The first rule in theset that matches is “triggered” and the ids in the right-hand side of the ruleprovide the names of the successor nodes in the next generation.

Node specification for the context sensitive patterns is similar to the contextfree method except that:

Page 248: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

the -> option is not part of the node declaration.if the node's id value is the same as the element itself then the elementitself can be specified in place of the node.

Each rule in a context sensitive rule set is a list of the form:

(id... -> id ...)

The -> rule operator divides each rule into two sides. The left-hand side of the ruledefines a “matching target” and the right-hand side defines the rewritesuccession. Either or both sides may contain more than one id. If the left-handside of the rule is a single id then the rule matches any node with the same id. Ifthe left-hand side has more than one id (a context-sensitive rule) then the rulematches if the strict predecessor in the left-hand side matches the current nodeand the adjacent nodes in the generation match the ids adjacent to the strictpredecessor. The strict predecessor id is notated as a list. Every context rulemust contain exactly one strict predecessor in its left hand side.

(1 (1) 2 -> 3)(1 * (2) -> 1 2)(5 (3) 3 4 -> )

The first rules specifies that node 1 rewrites to node 3 wherever 1 is preceded byitself and followed by 2 in the current generation. The second rule says that node2 rewrites to 1 and 2 whenever 1 occurs two positions earlier in the currentgeneration. The third rule means node 3 rewrites to nothing if preceded by 5 andfollowed by itself and 4 in the current generation. Note that the right-hand sidemay be empty and that the left-hand side may contain the wild card * whichmatches any single element in the current generation.

Example 20-25. Harmonies from Context sensitive rewrite.

(define (lsystem-harmonies len rhy dur) (let ((lsys (new rewrite :of '(((0 4 7) :id f) ((0 3 7) :id g) (+1 :id +) (-1 :id -)) :initially '(f + f - g - f) :rules '((f -> f f - g - - g - f) (g -> g f + f)))) (term false) (knum 60)) (process repeat len do (set! term false) (loop until term for x = (next lsys) do (if (list? x) (set! term x) (set! knum (fit (+ knum x) 48 72)))) each c in (transpose term knum) output (new midi :time (now) :keynum c :duration dur :amplitude .7) wait rhy)))

Page 249: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The lsystem-harmonies process uses a context sensitive rewrite pattern togenerate a harmonic grammar of chords and offsets that are then transposed tospecific pitches by the process. The variable lsys holds the pattern and knumholds the current transposition level for the process. There are four nodes in therewrite pattern, the first two define major and minor chords (0 4 7) and (0 3 7) thatare named F and G in the pattern, respectively. The other two nodes definetransposition increments of +1 and -1 and are named + and -. The list of termsspecified to :initially sets the first generation of the pattern to a specific list ofnode ids. Two rules are specified to the pattern, the first rule rewrites F terms andthe second G terms. The process uses a loop to read successive terms from thepattern until a major or minor chord list has been produced. If -1 or 1 isencountered during this process the loop adjusts the current offset level knum bythat amount. The fit function is used to ensure that this value remains within atwo octave range from key number 48 to 72. Once the loop has produced the nextchord list from the pattern the each clauses iterates every value in the chordtransposed to the current offset value in knum and outputs a midi object to play it.

Interaction 20-10. Listening to context-sensitive rewrite.

cm> (events (lsystem-harmonies 200 .2 .35) "pat-20.mid")"pat-20.mid"cm>

→ pat-20.mid Four channels.

Gloriette for John Cage

Gloriette for John Cage (1994) is a completely pattern based algorithmiccomposition. It was written in 1994 as a memorial tribute to John Cage and wascommissioned for the Busy Drone mechanical organ at the Stedelijk Museum inAmsterdam. The Busy Drone organ reads a large cardboard score that functionsjust like a piano roll except that the size and weight of the cardboard scorerestricts the mechanical organ to playing pieces that last only a few minutes induration. Keep in mind as you listen to the piece that every note you hear waspunched into heavy cardboard sheets by hand!

In keeping with the Cage's interest in aleatoric music, the main algorithm inthe piece uses a chance process in which the likelihood of the musical notes C,A,G, and E gradually increases as a function of time, causing the composer's nameto slowly emerge out of a background of G Dorian. The rhythmic scheme andnumber of voices are similarly inspired by the composer's name. Gloriette forJohn Cage was composed at the Zentrum fuer Kunst und Medientechnologie inKarlsruhe, Germany and has been included on several CDs.

→ gloriette.mp3 Gloriette for John Cage, performed by theBusy Drone mechanical organ at the Multimediale II Festival in

Page 250: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Karlsruhe Germany, 1994.

The code for a simplified version of this composition is show in Example 20-26.

Example 20-26. Probability as a function of time.

(define (cage offset) (let* ((w 0) ; weight of CAGE (d (pval w)) ; delay eval of w (p (new random :of ((g3 :weight ,d) ; delay weight of G (a3 :weight ,d) ; delay weight of A bf3 (c4 :weight ,d) ; delay weight of C d4 (e4 :weight ,d) ; delay weight of E f4 (g4 :weight ,d) ; delay weight of G (r :max 1 :weight .25)) ; add rests :for 1)) (q (new random :of (list 1/16 1/8 (new cycle :of 1/32 :for 2 ))))) (process repeat 100 for n = (next p) for r = (rhythm (next q) 65) for i from 0 ;; interpolate new weight value for CAGE notes. set w = (interp i 0 .5 90 4) output (new midi :time (now) :duration r :keynum (transpose n offset)) wait r)))

The cage process demonstrates how the probability of a random pattern can becontrolled as a function of time. Over the course of 100 iterations the notes C,A,Gand E gradually gain more probability than other notes in the G Dorian collection.The w variable controls the probability of the CAGE notes being selected relativeto the others. The variable d is a pval object that delays the evaluation of w andallows it to be placed inside the random pattern of notes. Each iteration of theprocess reads a note from the pattern and then sets w to a new interpolated value.The interpolation at the start of the process sets w to .5, so the CAGE notes at thestart of the process are only half as likely to be selected as the other notes in GDorian. The weight variable w is set to a new weight value for the CAGE notes oneach iteration of the process, so that each time a note is read from the pattern thevalue of the CAGE notes will have increased slightly over the previous iteration.By the 90th iteration the CAGE notes will be four times a likely as the others to beselected. Interaction 20-11 generates four cage processes in several differentoctaves, each offset by a half note a tempo 65.

Interaction 20-11. Listening to the Cage algorithm.

cm> (events (loop for o in '(-12 0 12 24)

Page 251: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

collect (cage o)) "pat-21.mid" (loop for r in '(0 h w w.) collect (rhythm r 65)))"pat-21.mid"cm>

→ pat-21.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file patterns.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

References

Dodge, C. & Jerse, T. (1985). Computer Music. New York: Schirmer Books.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 252: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

21Etudes, Op. 7: Automatic Jazz

In this Etude we examine a program that performs pattern-based jazzimprovisation. The code is derived from a program originally written by EricFlister at CCRMA, Stanford University, as a project for his undergraduatecomputer music class. (His original code has been simplified and adapted towork with General MIDI instruments.) Flister's improviser generates music for aMIDI jazz combo consisting of piano, acoustic bass and percussion parts. Our

first step will be to learn how to specify the appropriate MIDI program changesto establish the Jazz Combo instruments on three channels of the synthesizer.

MIDI Messages

Until this point we have worked exclusively with midi events — object-orientedrepresentations of MIDI note information. It may surprise you to learn that theseobjects are not actually defined in the MIDI protocol. Midi objects are high-level

abstractions that represent a pair of low-level MIDI messages. A MIDI messageis a packet of numbers sent to the synthesizer to control its operation. Thesemessages consists of a series of bytes, where each byte holds up to 7 bits of

information, enough to represent 27 different states, or the series of integersranging from 0 to 127. The two messages that a midi event represents arecalled Note On and Note Off messages. These messages are what MIDIkeyboards send when keys are depressed and released.

Common Music represents low-level MIDI messages as specially formattedintegers containing up to 3 MIDI bytes of information. All of the differentmessage types defined in the MIDI protocol are supported. Each of thesemessages is implemented by a constructor function, a predicate, and one ormore byte accessors, if appropriate. Interaction 21-1 demonstrates the creationand display of low-level MIDI Note On and Note off messages.

Interaction 21-1. Creating and displaying MIDI messages.

cm> (make-note-on 0 60 90)155226cm> (midi-print-message 155226)#<Note-On 0 60 90>155226cm> (make-note-off 9 48 127)2496639

Page 253: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

cm> (midi-print-message 2496639)#<Note-Off P "Hi-Mid-Tom" 127>2498175cm>

The Note On message was created with a channel value of 0, key number 60and key velocity 90. The utility function midi-print-message “decodes” low-level midi messages into a readable text format. The Note Off message wascreated with a channel value of 9, the channel that General MIDI reserves for thepercussion map (in zero based counting). The midi-print-message functionprints P for the channel number and also displays the sound associated with thekey number when a Note On or Note Off message references the drum map.

Program Changes

A MIDI program change message assigns a MIDI instrument (sometimesreferred to as a program, patch or tone) to a specific channel of the synthesizer.Program change values between 0-127 can be sent to any non-percussionchannel of a General MIDI synthesizer. The program change values needed forour Jazz Combo are 0, the Acoustic Grand Piano, and program 32, theAcoustic Bass. A program change is not necessary to select percussion partssince General MIDI already reserves channel 9 (zero-based) for the drum map.Interaction 21-2 shows the creation of our MIDI program changes using themake-program-change constructor.

Interaction 21-2. Program changes for Jazz Instruments

cm> (make-program-change 0 0)196608cm> (midi-print-message 196608)#<Program-Change 0 "Acoustic-Grand-Piano">196608cm> (make-program-change 1 32)462848cm> (midi-print-message 462848)#<Program-Change 1 "Acoustic-Bass">462848cm>

The constructor make-program-change takes a channel number and a programchange value and returns a MIDI program change message. midi-print-message includes the name of the instrument in its display.

The midimsg Object

The MIDI specification defines many different types of messages thatcollectively implement all of the ways that a MIDI synthesizer can be controlled.Common Music provides the midimsg object to represent these messages inhigh-level score generation. A midimsg object can hold any MIDI message

Page 254: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Class]

defined in the MIDI 1.0 specification. Midimsg objects can be computed alongwith midi events in score output.

midimsg

A midimsg is an object-oriented representation of a low-level MIDI message.midimsg supports the following slot initializations:

:time numberThe score time of the midimsg.

:msg integerThe 28 bit integer MIDI message.

:data listA list of any additional data bytes required by message.

We are now ready to define program changes for setting up the synthesizer toplay the Jazz Combo. We use two midimsg objects, the first sends a programchange 0 on channel 0 to select Acoustic Grand Piano and the second sendsprogram change 32 on channel 1 to select an Acoustic Bass. We use a seqobject to hold these events so that we can mix them into the output when itcomes time to generate the score.

Example 21-1. A seq of program changes for the Jazz

Combo

(new seq :name 'combo-setup :subobjects (list (new midimsg :time 0 :msg (make-program-change 0 0)) (new midimsg :time 0 :msg (make-program-change 1 32))))

We can test the setup by writing a score containing these program changesand two test tones:

Interaction 21-3. Testing the program changes.

cm> (events (list #&combo-setup (new midi :time 1 :channel 0 :keynum 60) (new midi :time 3 :channel 1 :keynum 48)) "pctest.mid")"pctest-1.mid"cm>

→ pctest-1.mid (two channels, program changes)

Playing the file should cause the Acoustic Grand Piano to play on channel 0and the Acoustic Bass on channel 1.

Page 255: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The Automatic Jazz Program

The automatic jazz program executes a “conductor” process. The conductorruns for a specified number of measures and sprouts piano, percussion andbass processes to improvise each measure. Each instrument process usesdata passed to it from the main process. This data includes the jazz scale toimprovise with, a transposition level for the jazz scale, a tempo factor and anoverall amplitude level. The amplitude level is adjusted on a per measure basisby the main conductor process. The other data are defined as global variablesthat can be adjusted and redefined by the composer.

Example 21-2. Global control data for the jazz improviser.

(define jazz-scale ; dorian with decorated octave '(0 2 3 5 7 9 10 12 14))

(define jazz-changes ; key changes '(bf3 ef4 bf3 bf ef4 ef bf3 bf f4 ef bf3 bf))

(define jazz-tempo 120)

We will examine each instrument process in detail before looking at the mainconductor algorithm.

The Percussion Parts

The percussion parts for the Jazz Combo consist of two ride cymbals, a highhat, snare and bass drums. We will introduce these parts in their order ofcomplexity, from simplest to most difficult. As mentioned in the previoussection, each percussion sound is addressed by a specific key number in theGeneral MIDI Drum Map. Example 21-3 contains the definition of thepercussion sounds we will use:

Example 21-3. Percussion key numbers in the GM drummap.

(define hihat 42) ; Closed Hi Hat(define snare 40) ; Electric Snare(define bdrum 35) ; Acoustic Bass Drum(define ride1 51) ; Ride Cymbal 1(define ride2 59) ; Ride Cymbal 2

The High Hat

The High Hat percussion process is very simple, it just plays the High Hat onthe second and fourth beats of every measure. Each hit sounds for the durationone triplet eighth note:

Page 256: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 21-4. The High Hat process.

(define (jazz-high-hat tmpo ampl) ;; generate a 4/4 measure of high-hat (let ((rhy (rhythm 'q tmpo)) (dur (rhythm 't8 tmpo)) (amp (amplitude 'mp)) (pat (new cycle :of (r ,hihat r ,hihat )))) (process repeat 4 output (new midi :time (now) :keynum (next pat) :channel 9 :duration dur :amplitude (* amp ampl)) wait rhy)))

The variable pat holds a cyclic pattern that rests (r) on the first and third beats,and plays the key number of hihat on the other second and fourth beat.

We now listen to eight measures of the High Hat. Since the processcomputes only one measure, we sprout eight versions of the process and offseteach copy by two seconds, exactly the duration of the combo's 4/4 measure attempo 120.

Interaction 21-4. Playing eight measures of the high hat.

cm> (events (loop repeat 8 collect (jazz-high-hat 120 1)) "perc.mid" '(0 2 4 6 8 10 12 14))"perc-1.mid"cm>

→ perc-1.mid

The Jazz Drums

The jazz-drums process is also quite simple. It divides each beat into a tripletquarter followed by a triplet eight and then, on each note, randomly selectsbetween playing the snare, the bass drum or resting.

Example 21-5. Jazz drums

(define (jazz-drums tmpo ampl) (let ((rhys (new cycle :of '(t4 t8))) (amps (new random :of '(f (ffff :weight .1)))) (knums (new random :of ((r :weight .25) ,snare ,bdrum)))) (process repeat 8 for k = (next knums) for a = (amplitude (next amps)) for r = (rhythm (next rhys) tmpo) output (new midi :time (now)

Page 257: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:keynum k :channel 9 :duration r :amplitude (* a ampl)) wait r)))

The knums variable holds the random pattern that selects between the snare,the bass drum or the rest value r. The probability of resting is .25, which meansthat resting is only one quarter as likely as playing either drum. The amplitudesof the drums are generally forte but one tenth of the time the process producesa very loud tone.

Interaction 21-5. Playing eight measures of the drums.

cm> (events (loop repeat 8 collect (list (jazz-high-hat 120 .99) (jazz-drums 120 .99))) "perc.mid" '(0 2 4 6 8 10 12 14))"perc-2.mid"cm>

→ perc-2.mid

The Jazz Cymbals

The jazz-cymbals process is slightly more complex than either the high hat orthe drums. The cymbals process performs a constant stream of triplet eighths in

which the ride1 cymbal is played on the beginning or every quarter note. Thesecond and third triplets of each beat are either rests or a random choice

between ride1, ride2 or a rest. Table 21-1 shows the beat map for a measure

of the process, where 1 means the ride1 cymbal is played, r means a rest and x

means a random choice between ride1, ride2 or a rest:

Table 21-1. Beat map for one measure of the ride cymbals.

triplet 1 2 3 4 5 6 7 8 9 10 11 12

cymbals 1 r x 1 r 1 1 x x 1 x 1

The random elements marked x in Table 21-1 are created by a helper functioncalled or12r:

Example 21-6. Random cymbal element.

(define (or12r wt) ;; wt is weight of resting relative to playing ;; r1 pattern plays ride1 or rests ;; r2 pattern plays ride2 or rests (let ((r1 (new random

Page 258: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

:of (,ride1 (r :weight ,wt) ) :for 1)) (r2 (new random :of (,ride2 (r :weight ,wt) ) :for 1))) ;; return random pattern that slightly ;; prefers r1 pattern over r2 pattern (new random :of ((,r1 :weight 1.5) ,r2) :for 1)))

The or12r function returns a pattern that randomly selects between two sub-

patterns, r1 or r2. Each sub-pattern, in turn, randomly selects between playing a

cymbal (ride1 in the case of r1 and ride2 in the case of r2) or a rest. The

probability weight wt is the probability of playing a rest relative to playing eitherone of the ride cymbals. The pattern returned by or12r assigns the overall

weight of 1.5 to the r1 sub-pattern, which means that probability of r1 relative to

r2 is 3:2.Given this helper function, the jazz-cymbals process creates the pattern

described in Table 21-1 to improvise one measure of music:

Example 21-7. Jazz cymbals.

(define (jazz-cymbals tmpo ampl) (let* ((rhy (rhythm 't8 tmpo)) (amps (new cycle :of '(mf mp fff f mp ffff mf mp fff f mp ffff))) (knums (new cycle :of (list ride1 'r (or12r 5) ride1 'r ride1 ride1 (or12r 7) (or12r 7) ride1 (or12r 3) ride1)))) (process repeat 12 for k = (next knums) for a = (amplitude (next amps)) output (new midi :time (now) :keynum k :channel 9 :duration rhy :amplitude (* a ampl )) wait rhy)))

The knums variable holds the random pattern that produces ride1, ride2 or therandom choices created by the helper function. We can now listen eight bars ofall the percussion together:

Interaction 21-6. Listening to the percussion sounds.

cm> (events (loop repeat 8 collect

Page 259: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(list (jazz-high-hat 120 1) (jazz-drums 120 1) (jazz-cymbals 120 1))) "perc.mid" '(0 2 4 6 8 10 12 14))"perc-3.mid"cm>

→ perc-3.mid

The Jazz Piano Part

The jazz piano plays “improvised” jazz chords based on a pattern of rootchanges and a scale pattern that is transposed to each root. The pianorandomly choose between playing triplet eighths or straight eights for a givenmeasure.

Example 21-8. The jazz piano process.

(define (jazz-piano scale on tmpo ampl) ;; generate a measure of jazz harmony. ;; measure contains either 8 or 12 notes. (let* ((reps (odds .65 8 12)) (rhys (if (= reps 8) (new cycle :of '(t4 t8)) (new cycle :of 't8))) (amps (if (= reps 8) (new random :of (list (new cycle :of '(mp f)) (new cycle :of '(mf ff)))) (new random :of (list (new cycle :of '(mp p f)) (new cycle :of '(mf mp ff)))))) (knms (new random :of ((,(new heap :of scale :for (new random :of '(1 2 3 4))) :weight ,(new random :of '(1.15 1.65))) r)))) (process repeat reps for r = (rhythm (next rhys) tmpo) for a = (amplitude (next amps)) for l = (transpose (next knms true) on) each k in l output (new midi :time (now) :channel 0 :keynum k :duration r :amplitude (* a ampl)) wait r)))

The jazz-piano process creates a measure of jazz harmonies given a scale, a

transposition level on, a tempo factor tmpo and an amplitude level ampl thatscales the local amplitude value produced in the process. Each measure of the

Page 260: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

jazz-piano part will contain reps number of notes as determined by the oddsfunction, which in this process chooses 8 notes per measure approximately65% of the time, otherwise 12 notes. The rhys variable is set to a pattern of

rhythms that depends on the value of reps. If the piano plays 8 notes in themeasure then the rhythmic pattern for the process will consists of triplet quarter(tq) followed by a triplet 8ths (te), otherwise the piano will play twelve triplet 8ths.The harmonies are generated by a random pattern that selects between a restand a heap of notes created from the scale that was passed into process.Probability of choosing a note is either 1.16 or 1.65 relative to the rest andeach time the heap is selected it will generate one to four notes.

The process iterates reps times. On each iteration it selects a new rhythm,amplitude and list of key numbers to play. The key numbers are always returnedin a list because the next function is called with its optional second argumentset to true, which means that an entire period of key numbers is returned eachtime. This list will therefore hold between one and four key numbers on each

iteration and the each clause iterates every key number k in the list and outputsa midi event to play it. Since the key numbers are all output at the same currenttime the piano produces up to four simultaneous notes on every rhythm in themeasure. We now listen to the piano part in isolation. We will create fourmeasures using the jazz-scale we defined transposed to bf3 at tempo 120.

Interaction 21-7. Playing four measures of the piano part.

cm> (events (loop repeat 4 collect (jazz-piano jazz-scale 'bf3 120 1)) "piano.mid" '(0 2 4 6))"piano-1.mid"cm>

→ piano-1.mid

The Acoustic Bass Part

The acoustic bass part is perhaps the most complex in terms of itsimplementation. The bass part plays a melodic line built out of tones from thejazz-scale's tonic seventh chord alternating with color tones outside the tonicchord. The process first divides the jazz-scale (0 2 3 5 7 9 10 11 12 14) intotwo sets: the tonic set, designated “t” in the code below, contains the tonic

seventh pitches 0 2, 4, 6 and 7, and the color set, designated c, which containsthe decoration pitches 1, 3, 5, 7 and 9. The bass plays a series of 12 tripletsper measure, on each triplet only one of the two sets is possible. On all but thefirst triplet a rest is also possible. A tonic note always sounds on the 1st tripleteighth of the measure. But in the next three groups of three triplets there are fivealternate patterns possible, only one of which is selected at random to beplayed in the measure. The top line in Table 21-2 shows the triplet 8th sub-groupings within the measure, and the five possible choices for each groupappear on the lines below it. Each of these lines, labeled A through E,designates the probability of selection from most to least likely. Within eachtriplet grouping, the letter r stands for rest, t stands for a tonic note and c stands

Page 261: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

for a color note. The 11th and 12th triplets in the measure are simply randomchoices between either a tonic note and a rest (t|r) or a color note and a rest(c|r)

Table 21-2. Possible sub-patterns a-e within one

measure's triplet beat map.

1 2-4 5-7 8-10 11 12

a t r r c r r t r r c t|r c|r

b t r r r r r r r t c t|r c|r

c t r t c r c t r r r t|r c|r

d t c t c t c t c t c t|r c|r

e t c t r t c r c t r t|r c|r

The acoustic-bass process defines two helper functions to assist in patterncreation:

Example 21-9. Helper functions for the acoustic bass

process.

(define (getset scale ints) ;; return the notes in scale ;; at the positions in ints. (loop for i in ints collect (list-ref scale i)))

(define (rancyc data prob) ;; create cyclic pattern to be used ;; as element in random pattern with weight prob (list (new cycle :of data) :weight prob))

The getset function returns the list of notes in scale whose indices are

specified in the list ints. The rancyc function creates the sub-pattern groupsshown in Table 21-2. It returns a pattern node whose element is a cyclic pattern

with an associated probability weight prob in the surrounding random pattern.The jazz-bass process uses the helper functions to create the pattern data.

It first uses getset to partition the jazz-scale into two random patterns, tonics

and colors. The bmap variable holds the “beat map” for the measure. Thepattern will returns twelve values, each value is either the symbol t c or r. The t

value means the process should read a note from the tonic pattern, c means toread from the color pattern and r means to rest. The beatmap divides the twelvetriplets of the measure into pattern subgroups containing 4+3+3+1+1 values.Each of first three subgroups chooses one of five possible patterns created bythe rancyc helper function.

Example 21-10. The acoustic bass process.

Page 262: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define (jazz-bass scale on tmpo ampl) (let ((rhy (rhythm 'te tmpo)) (tonics (new random :of (getset scale '(0 2 4 6 7)))) (colors (new random :of (getset scale '(1 3 5 6 8)))) (amps (new cycle :of '(mp p ffff fff p fff mp p ffff fff mp fff))) (durs (new cycle :of '(tq t8 t8))) ;; beat map (bmap (new cycle :of (list ;; 5 possible patterns for 1-4 (new random :for 1 :of (list (rancyc '(t r r c) 1.0) (rancyc '(t r r r) .25) (rancyc '(t r t c) .22) (rancyc '(t c t c) .065) (rancyc '(t c t r) .014))) ;; 5 possible patterns for 5-7 (new random :for 1 :of (list (rancyc '(r r t) 1.0) (rancyc '(r r r) .25) (rancyc '(r c t) .22) (rancyc '(t c t) .038) (rancyc '(t c r) .007))) ;; 5 possible patterns for 8-10 (new random :for 1 :of (list (rancyc '(r r c) 1.0) (rancyc '(r t c) .415) (rancyc '(r r r) .25) (rancyc '(c t c) .11) (rancyc '(c t r) .018))) ;; two possible values for 11 (new random :for 1 :of '((r :weight 1) (t :weight .25))) ;; two possible values for 12 (new random :for 1 :of '((r :weight 1) (c :weight .25))))))) (process repeat 12 for x = (next bmap) for k = (if (equal? x 't) (next tonics) (if (equal? x 'c) (next colors) x)) for d = (rhythm (next durs) tmpo) for a = (amplitude (next amps)) output (new midi :time (now) ; :keynum (transpose k on) :channel 1 :duration d :amplitude (* a ampl)) wait rhy)))

Page 263: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The process plays twelve notes per measure, on each note a value x is readfrom the beat map. If x is either t or c then k is set to a value from the tonic orcolor pattern, otherwise x is a rest (r). The key number of each midi event is

determined by transposing the scale degree k to the transposition level onspecified to the process. We now listen to eight measures of jazz-bass inisolation:

Interaction 21-8. Listening to eight bars of the acousticbass.

cm> (events (loop repeat 8 collect (jazz-bass jazz-scale 'bf2 120 1)) "bass.mid" '(0 2 4 6 8 10 12 14))"bass-1.mid"cm>

→ bass-1.mid

The Main Process

The main process of the automatic jazz program is quite simple: jazz-combo

iterates measures number of times and sprouts the combo's instrument at thestart of each measure. Every 12 measures Jazz-combo adjusts its overallamplitude level so that the combo's amplitude changes dynamically during theperformance. Similar adjustments could be easily made to the tempo, scaleand root movements of the jazz improvisation. These improvements are left tothe reader to implement.

Example 21-11. The main process.

(define (jazz-combo measures changes tempo scale) (let ((roots (new cycle :of changes)) (ampl 1)) (process for meas below measures for root = (next roots) if (= 0 (mod meas 12)) set ampl = (between .5 1) sprout (jazz-piano scale root tempo ampl) sprout (jazz-cymbals tempo ampl) sprout (jazz-high-hat tempo ampl) sprout (jazz-drums tempo ampl) sprout (jazz-bass scale (transpose root -12) tempo ampl) wait (rhythm 'w tempo))))

We now generate a 24 bar improvisation from the jazz combo. Soundexamples from two different runs are provided.

Interaction 21-9. Two different versions of the Jazz Combo.

Page 264: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

cm> (events (list #&combo-setup (jazz-combo 48 jazz-changes jazz-tempo jazz-scale)) "jazz.mid")"jazz-1.mid"cm> (events (list #&combo-setup (jazz-combo 48 jazz-changes jazz-tempo jazz-scale)) "jazz.mid")"jazz-2.mid"cm>

→ jazz-1.mid→ jazz-2.mid

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file jazz.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 265: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

22Etudes, Op. 8: An AlgorithmicModel of György Ligeti's Désordre

In this Etude we look at a pattern-based description of György Ligeti's firstPiano Etude, Désordre. The program examples and most of the commentary

were written by Tobias Kunze at CCRMA, Stanford University. Désordre hasbeen analyzed in great depth by Kinzler in György Ligeti: decision and

automatism in `Désordre', 1er Étude, Premier Livre (Kinzler) (Interface, Swets &Zeitlinger, 1991) and Jane Piper Clendinning's The Pattern MeccanicoCompositions of György Ligeti (Clendinning) (Perspectives of New Music)discusses Ligeti's use of patterns in a number of different compositions.

The intent of this Etude is to consider Ligeti's composition from theperspective of algorithmic control — to arrive at some idea of what specific“algorithmic knobs” could be implemented that might offer a possible analog tothe composer's actual decisions. We first present a general analysis of theEtude's essential features, and then model the composition in software usingprocess descriptions. The model makes an honest attempt to be realistic buttrades off accuracy against flexibility. After all, there is no point in implementingan algorithm that cannot be manipulated or adjusted so that it can exploredifferent paths through a composition's theoretical parameter space.

A note regarding references to the score: since the left and right hands areessentially independent, each point of interest in the score will be indicated by

page, system and measure numbers. Page numbers are prefixed by p,

systems by s and measures by m. For example “p3s2m1” stands for page 3,system 2. measure 1. Where necessary, an indication of the part will also beprovided. In systems starting with a partial measure, the first complete measureis counted as measure 1. All numbers follow the facsimile edition of Étudespour piano: premier livre. published by B. Schott's Söhne, Mainz, 1985.

Structure

The textural make-up of the Etude is rather straightforward. Each hand isassigned its own part and both parts share a common, layered substructureand a continuously pulsating eighth-note motion. Each hand is restricted to aparticular set of notes: the right hand plays only the white keys and the left handplays only black keys. In addition to establishing the essential dramaticopposition in the piece, the partitioning also allows the relatively difficult parts to

Page 266: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

occupy the same register at certain points in the composition. In keeping withthe harmonic partitioning, each part follows its own internal logic and isessentially independent from the other “horizontally” as well. That is, both handsare largely unsynchronized on the metrical level although stretches with semi-synchronized phrase structures do exist. This metrical independence is centralto the composition of Désordre and most of the form-defining features of thework stem from this basic independence.

The internal structure of each part is a combination of constantly risingeighth notes in the background and a rhythmically pronounced, slowerforeground, all within the constant running motion of the eighth notes. The

foreground layer is played forte throughout the piece and heavily accented. Theforeground layer in both hands features a phrase structure that is reminiscent oftraditional A-A'-B forms, where two closely related shorter phrases are followedby a longer developmental third phrase. In spite of the distortion by theasymmetrical meter employed in each part, the phrasing hints at an underlyingsimple and song-like two-beat meter. The similarly asymmetric “meter” that isinduced in the backgrounds by eighth-note groups of differing lengths isrelatively negligible compared to the prominent metric structure in theforeground layers.

On a formal level, the piece is structured by both the temporal behavior andpitch behavior of its two parts. A first section, 404 eighth notes long, (from thebeginning of the piece to p3s4m4) is quite static in tempo except for the slightaccelerando towards the end. The next section, which extends until p4s4m7(231 eighth notes in duration) accelerates the foreground parts until they arereduced to eighth notes, at which point a typical “ligetiesque” cut interrupts thelower part (which had by then hit the bottom of the piano range) and resumes inthe descant as well as in the original foreground tempo. This third section (429measures) extends to the end and is characterized by a static tempo in theupper part and a slowing down of the lower part.

The Patterns

The foreground in both the upper and lower parts will consist of cyclicrepetitions of a constant, stepwise pattern. Within each cycle the pattern istransposed diatonically by a constant amount. The pattern for the upper partconsists of 26 steps, 7 for each of the A phrases and 12 for phrase B:

Table 22-1. Step Pattern for white keys.

Phrase A 0 0 1 0 2 1 -1

Phrase A' -1 -1 2 1 3 2 -2

Phrase B 2 2 4 3 5 4 -1 0 3 2 6 5

The pattern for the lower part has 33 steps, again 7 for each of the A phrasesand 19 in the extended phrase B:

Table 22-2. Step Pattern for black keys

Page 267: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Phrase A 0 0 1 0 2 2 0

Phrase A' 1 1 2 1 -2 -2 -1

Phrase B 1 1 2 2 0 -1 -4 -3 0 -1 3 2 1 -1 0 -3 -2 -3 -5

There are a total of 14 cycles in the upper part, each of which is transposed

diatonically one step upwards. Due to its greater length, there only 11 cycles inthe lower part. The smaller number of cycles — which translates into fewertranspositions — is more than compensated for by (1) a two-step diatonictransposition downwards and (2) the lower part's pentatonic mode having fewersteps per octave than the heptatonic mode of the upper part. The cycles alignwith the score as shown in Table 22-2

Table 22-3. Cycles and transpositions for both parts.

Lower Part Upper Part

# Transp. Score # Transp. Score

Section I

1: ds3 p2s1m0 1: b4 p2s1m0

2: as2 p2s3m5 2: c5 p2s2m7

3: fs2 p3s2m2 3: d5 p2s4m7

4: e5 p3s2m6

Section II

4: cs2 p3s4m4 5: f5 p3s4m4

5: gs1 p4s1m3 6: g5 p3s4m11

6: ds1 p4s2m3 7: a5 p4s1m7

7: as0 p4s3m2 8: b5 p4s2m5

8: fs0 p4s4m11 9: c6 p4s3m3

10: d6 p4s3m10

Section III

(p4s4m7) 11: e6 p4s4m7

9: cs5 p5s1m2 12: f6 p5s2m4

10: gs4 p5s3m7 13: g6 p5s4m4

11: ds4 p6s2m42 14: a6 p6s2m4 3

1. Cycle 8 in the lower part reaches the bottom of thepiano range and discontinues after m. 4 in phrase Bonly to resume immediately in the treble clef (p4s4m7).The first note of this continuation should read fs but forobvious reasons is given as fs5 to the right hand.

2. Cycle is cut off after m. 3 in Phrase B.3. Cycle is cut off after m. 2 in Phrase A'.

The rhythmic picture is less clear due to rasterization effects in the accelerationand deceleration of the patterns. The sequence of rhythms in the upper and

Page 268: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

lower parts read as follows:

Table 22-4. Upper part, foreground rhythms.

Sect. I: Little Disorder (404 eighths)

3 5 3 5 5 3 73 5 3 5 5 3 73 5 3 5 5 3 3 4 5 3 3 5

cycle 1

3 5 3 4 5 3 83 5 3 4 5 3 83 5 3 4 5 3 3 5 5 3 3 4

cycle 2

3 5 3 5 5 3 73 5 3 5 5 3 73 5 3 5 5 3 3 4 5 3 3 5

cycle 3

3 5 3 4 5 2 72 4 2 4 4 2 52 3 2 3 3 1 1 3 3 1 1 3

cycle 4 (accel.)

Sect. II: Rapid accelerando (231 eighths)

1 2 1 2 2 1 31 2 1 2 2 1 31 2 1 2 2 1 1 2 2 1 1 2

cycle 5

1 2 1 2 2 1 31 2 1 2 2 1 31 2 1 2 2 1 1 2 2 1 1 2

cycle 6

1 2 1 2 2 1 31 2 1 2 2 1 21 2 1 2 2 1 1 2 2 1 1 2

cycle 7

1 2 1 2 2 1 21 2 1 2 2 1 21 2 1 2 2 1 1 2 2 1 1 2

cycle 8

1 2 1 2 2 1 21 2 1 2 1 1 21 2 1 2 2 1 1 1 2 1 1 1

cycle 9

1 2 1 1 1 1 21 1 1 1 1 1 21 1 1 1 1 1 1 1 1 1 1 1

cycle 10

Sect. III: Static (429 eighths)

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 5 3 3 5

cycle 11

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 5 3 3 5

cycle 12 (same)

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 5 3 3 5

cycle 13 (same)

3 5 3 5 5 3 8 cycle 14

Page 269: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

3 5 3 5 5 3 83 5 3 5 5 8 (cuts off here)

Table 22-5. Lower part, foreground rhythms.

Sect. I: Static (404 eighths)

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 5 3 3 5 3 5 3 5 5 3 8

cycle 1

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 5 3 3 5 3 5 3 5 5 3 8

cycle 2

3 5 3 5 5 3 83 5 3 5 5 2 73 4 3 4 4 2 2 4 4 2 2 3 2 3 1 3 3 1 4

cycle 3(accel.)

Sect. II: Rapid Accelerando (231 eighths)

1 3 1 2 2 1 31 2 1 2 2 1 31 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 3

cycle 4

1 3 1 2 2 1 31 2 1 2 2 1 31 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2

cycle 5

1 2 1 2 2 1 21 2 1 2 2 1 21 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2

cycle 6

1 2 1 2 2 1 21 2 1 2 2 1 21 2 1 2 2 1 1 2 1 1 1 2 1 1 1 1 1 1 2

cycle 7

1 1 1 1 1 1 21 1 1 1 1 1 21 1 1 1 1 1 1 1

cycle 8

Sect. III: Allargando (429 eighths)

5 3 3 5 3 5 3 5 5 3 8

3 5 3 5 5 3 83 5 3 5 5 3 83 5 3 5 5 3 3 5 6 3 3 5 3 5 3 6 5 3 8

cycle 9

3 6 3 5 5 3 93 5 3 5 6 3 83 5 3 6 5 3 3 5 6 3 3 5 3 5 3 6 5 3 9

cycle 10

3 7 3 8 9 3 133 11 3 21

cycle 11

The Model

The brief analysis presented in the previous section will allow us to translate the

Page 270: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

combined foreground structure of Désordre into pattern descriptions. We beginby defining an 8th note pulse for the piece according to the specification in thescore. A helper function eighth-time will allow us to convert from durationvalues expressed as number of 8th notes into time in seconds:

Example 22-1. Global tempo and amplitude.

(define eighth-pulse (rhythm 'e 76.0 'w))

(define (eighth-time number-of-eighths) ;; Convert duration in eighth notes ;; to time in seconds (* eighth-pulse number-of-eighths))

(define fg-amp .7) ; foreground amp

(define bg-amp .5) ; background amp

The global variable eighth-pulse holds the duration in seconds of an 8th notepulse at tempo 76 with a whole note beat. This value works out to 15/152, orapproximately 0.098 seconds per 8th note. The function eighth-time translates

a specified number-of-eighths into time in seconds by multiplying that number

by the speed of one eighth note. The variables fg-amp and bg-amp defineglobal amplitude levels for the foreground and background processes,respectively. These can be adjusted as needed to increase or decrease theamplitude level of the foreground relative to the background.

Upper Foreground Materials

As suggested by our analysis in the preceding section, the algorithms to

implement the upper and lower parts to Désordre will be driven by four sets ofdata: the part's mode, its step pattern, its transposition pattern, and its rhythmicpattern.

Upper Foreground Melody

We examine the melodic materials first. Example 22-2 contains the definitions

of the white key mode and the step pattern for the upper part. A step is an

interval that describes relative melodic motion between notes, i.e. the intervallicdistances between successive notes in a sequence.

Example 22-2. Upper foreground mode and step patterns.

(define white-mode (new mode :steps '(c d e f g a b c)))

(define white-fg-steps '(0 0 1 0 2 1 -1 ; Phrase a -1 -1 2 1 3 2 -2 ; Phrase a'

Page 271: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

2 2 4 3 5 4 -1 0 3 2 6 5)) ; Phrase b

(define (make-white-fg-notes note) ;; convert note in MIDI scale to modal equivalent (let ((step (keynum note :to white-mode))) ;; pattern of steps is offset ;; from a constantly rising offset (new transposer :of (new cycle :of white-fg-steps) :by (new range :initially step :by 1))))

The white-mode global variable is set to a mode object that implements thewhite key mode for the upper part. Each octave has only eight tones, andadjacent key numbers in the mode will be either one or two semi-tones apart inthe chromatic scale. The white-fg-steps variable contains the sequence of step

motion that describes the upper part. Each step in white-fg-steps describes the

relative distance from the previous step. For example, if the initial position forthe step pattern were c4 then the steps (0 0 1 0 2) would produce the notes (c4

c4 cs cs ds) in the chromatic scale but (c4 c4 d4 d4 e4) in white-mode.The make-white-fg-note function returns a pattern that implements the

melodic foreground of the upper part. The starting point for the melody is the

note specified to the function which is converted to its key number equivalent in

white-mode. The transposer pattern transposes a cycle pattern of foregroundsteps (intervals) onto the diatonic degrees of the white mode.

Upper Foreground Rhythmic Pattern

For reasons of clarity the rhythmic pattern for the upper part is presented herein its entirety even though is is fairly long. It would certainly be possible to definethis pattern in terms of repeated subpatterns.

Example 22-3. The upper foreground rhythmic pattern.

(define (make-white-fg-rhythms) (new cycle :of (3 5 3 5 5 3 7 ; cycle 1 3 5 3 5 5 3 7 3 5 3 5 5 3 3 4 5 3 3 5 3 5 3 4 5 3 8 ; cycle 2 3 5 3 4 5 3 8 3 5 3 4 5 3 3 5 5 3 3 4 3 5 3 5 5 3 7 ; cycle 3 3 5 3 5 5 3 7 3 5 3 5 5 3 3 4 5 3 3 5 3 5 3 4 5 2 7 ; cycle 4 2 4 2 4 4 2 5 2 3 2 3 3 1 1 3 3 1 1 3 1 2 1 2 2 1 3 ; cycle 5 1 2 1 2 2 1 3 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 3 ; cycle 6 1 2 1 2 2 1 3 1 2 1 2 2 1 1 2 2 1 1 2

Page 272: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

1 2 1 2 2 1 3 ; cycle 7 1 2 1 2 2 1 2 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2 ; cycle 8 1 2 1 2 2 1 2 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2 ; cycle 9 1 2 1 2 1 1 2 1 2 1 2 2 1 1 1 2 1 1 1 1 2 1 1 1 1 2 ; cycle 10 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 ,(new cycle :of (new cycle :of '(3 5 3 5 5 3 8 ; cycle 11-14 3 5 3 5 5 3 8 3 5 3 5 5 3 3 5 5 3 3 5)) :for 3) 3 5 3 5 5 3 8 ; cycle 14 3 5 3 5 5 3 8 3 5 3 5 5 3))) ; cuts off here

Lower Foreground Materials

The algorithm for the lower foreground part is driven by the same types of data.However, since it is eventually shifted into the treble clef after it hits the bottomof the keyboard (p4s4m7), its transposition pattern adds a “warp” thattransposes the whole pattern up five octaves, or 20 black mode steps (see the

warp-point variable in Example 22-4).

Lower Foreground Melody

Our lower foreground melody is defined in a manner analogous to the upperforeground, except of course that the pattern and mode are quite different. Theimplementation is made slightly more complex because the lower pattern“warps”, or shifts, when it reaches the bottom of the keyboard by jumping backup to the middle register of the keyboard.

Example 22-4. Lower foreground mode and step patterns.

(define black-mode (new mode :steps '(cs ds fs gs as cs)))

(define black-fg-steps '(0 0 1 0 2 2 0 ; Phrase a 1 1 2 1 -2 -2 -1 ; Phrase a' 1 1 2 2 0 -1 -4 -3 0 ; Phrase b 0 -1 3 2 1 -1 0 -3 -2 -3 -5))

(define (make-black-fg-notes note) (let* ((cycle-length (length black-fg-steps))

Page 273: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(warp-point (+ (* cycle-length 7) 7 7 8)) (step (keynum note :to black-mode))) (new transposer :of (new transposer :of (new cycle :of black-fg-steps) :stepping (new line :of (list (new cycle :of 0 :for warp-point) 20))) :by (new range :initially step :by -2))))

The warp-point variable specifies the point at which the lower process jumpsfrom the bottom of the keyboard up to the middle register. This is implementedusing two transposer patterns. The inner transposer replicates each cycle ofthe step relative to an offset generated from a line pattern. A line produceselements in sequential order but sticks on the last value. Thus the line specified

in Example 22-4 generates no offset (returns 0) until warp-point but from thatpoint onward produces the warp transposition 20, thus transposing each value

produced from black-fg-steps cycle by 20 from that point onward. Thesurrounding pattern then applies the descending stepwise transposition (-2) toeach period of the inner pattern.

Lower Foreground Rhythm

The definition of the lower foreground rhythmic pattern is derived directly fromthe data in Table 22-4. There is enough inner repetition to make the definitionof subpatterns worthwhile.

Example 22-5. The lower foreground rhythmic pattern.

(define (make-black-fg-rhythms) (new cycle :of '(3 5 3 5 5 3 8 ; cycle 1 3 5 3 5 5 3 8 3 5 3 5 5 3 3 5 5 3 3 5 3 5 3 5 5 3 8 3 5 3 5 5 3 8 ; cycle 2 3 5 3 5 5 3 8 3 5 3 5 5 3 3 5 5 3 3 5 3 5 3 5 5 3 8 3 5 3 5 5 3 8 ; cycle 3 3 5 3 5 5 2 7 3 4 3 4 4 2 2 4 4 2 2 3 2 3 1 3 3 1 4 1 3 1 2 2 1 3 ; cycle 4 1 2 1 2 2 1 3 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 3 1 3 1 2 2 1 3 ; cycle 5 1 2 1 2 2 1 3 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2 1 2 1 2 2 1 2 ; cycle 6 1 2 1 2 2 1 2 1 2 1 2 2 1 1 2 2 1 1 2 1 2 1 2 2 1 2

Page 274: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

1 2 1 2 2 1 2 ; cycle 7 1 2 1 2 2 1 2 1 2 1 2 2 1 1 2 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 2 ; cycle 8 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 5 3 3 5 3 5 3 5 5 3 8 3 5 3 5 5 3 8 ; cycle 9 3 5 3 5 5 3 8 3 5 3 5 5 3 3 5 6 3 3 5 3 5 3 6 5 3 8 3 6 3 5 5 3 9 ; cycle 10 3 5 3 5 6 3 8 3 5 3 6 5 3 3 5 6 3 3 5 3 5 3 6 5 3 9 3 7 3 8 9 3 13 ; cycle 11 3 11 3 21))) ; cuts off here

Foreground Process Description

We are now ready to define a process that will play our foreground material:

Example 22-6. Foreground process.

(define (fg-mono mode keynums rhythms) (process for key = (keynum (next keynums) :from mode) for rhy = (eighth-time (next rhythms)) output (new midi :time (now) :keynum key :amplitude fg-amp :duration rhy) wait rhy until (eop? rhythms)))

The fg-mono process takes a mode, a pattern of keynums and a pattern of

rhythms. The process generates a melody with every keynum key and rhythm

rhy in the patterns for one rhythmic period. Since the pattern keynums that ispassed to the function describes relative intervallic motion within a particularmode, the :from keyword argument to keynum is used to convert scaler

positions in mode into their equivalent positions in the standard chromaticscale used by MIDI. Since this mode is passed into the process, fg-mono is acompletely generic description that can be used to generate both the upper andlower foreground parts. We now listen to each foreground part, first individuallyand then mixed together.

Interaction 22-1. Play upper foreground.

cm> (events (fg-mono white-mode (make-white-fg-notes 'b4) (make-white-fg-rhythms)) "ligeti.mid")"ligeti-1.mid"cm>

Page 275: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

→ ligeti-1.mid

Interaction 22-2. Play lower foreground.

cm> (events (fg-mono black-mode (make-black-fg-notes 'ds4) (make-black-fg-rhythms)) "ligeti.mid")"ligeti-2.mid"cm>

→ ligeti-2.mid

Interaction 22-3. Play both foregrounds together.

cm> (events (list (fg-mono white-mode (make-white-fg-notes 'b4) (make-white-fg-rhythms)) (fg-mono black-mode (make-black-fg-notes 'ds4) (make-black-fg-rhythms))) "ligeti.mid")"ligeti-3.mid"cm>

→ ligeti-3.mid

Enhancement 1

The first improvement to our model will add octaves to the foreground andsimulate a background layer by filling in the 8th note pulse using randomlyselected mode notes centered around each of the foreground notes.

Example 22-7. Add octaves and a fake background

(define (D&eacute;sordre-w/octaves mode ntes rhys) (let ((fg-time 0) (fg-eighths 0) (mode-deg 0)) (list ;; foreground process (process with dur and key set fg-time = (now) set mode-deg = (next ntes) set key = (keynum mode-deg :from mode) set fg-eighths = (next rhys) set dur = (eighth-time fg-eighths) output (new midi :time fg-time :keynum key :duration (- dur .01) :amplitude fg-amp)

Page 276: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

output (new midi :time fg-time :keynum (+ key 12) :duration (- dur .01) :amplitude fg-amp) wait dur until (eop? rhys)) ;; background process fills in 8th note pulses with ;; randomly selected mode notes based on current ;; foreground note. (let ((pat (new range :from (pval mode-deg) :stepping (new random :of '((1 :weight 3) 2 (3 :weight .5 :max 1))) :for (pval fg-eighths)))) (process repeat 1064 for k = (keynum (next pat) :from mode) unless (or (= (now) fg-time) (not (<= 0 k 127))) output (new midi :time (now) :keynum k :duration (- eighth-pulse .01) :amplitude bg-amp) wait eighth-pulse)))))

The Désordre-w/octaves process takes the same arguments as our originalplay-fg process but its definition is more involved. Notice that Désordre-w/octaves returns a list of two processes, the first produces our foregroundmaterials and the second generates the background. The first process isequivalent to our original play-fg definition except that it outputs two MIDIobjects per interaction to create the octave passage work. Our first processdescription sets two shared variables that the second process will use too. Themode-deg variable holds the current mode degree of the foreground and the

variable fg-eighths is set to the duration (in eighth notes) of the note theprocess is currently playing.

The second process description creates a harmonic background to support

the foreground material. It creates a pattern pat that generates random stepsfrom whatever mode-deg the first process is currently playing. The rangepattern produces each step using a random pattern that generates threedifferent step sizes: steps to adjacent degrees (1), steps that skip one degree(2) and steps that skip two degrees (3). The actual intervallic distances thattheses steps produce will depend on the mode to which it is applied. Forexample a single step (1) can produce either half steps or whole steps in ourwhite mode but whole steps or minor thirds in our pentatonic black mode. Sincethe range's initial position as well as its period length depend on whatever thefirst process is currently generating, the pattern must uses pval to delay theevaluation of fg-eighths and mode-deg until the pattern is actually running.

Page 277: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Otherwise, these variables would be evaluated when the pattern is created, notrather then when the pattern was used.

The rest of the process description is relatively straightforward. Because theforeground material extends to the top and bottom of the keyboard, our randombackground process could potentially generate random key number above orbelow the legal range of key numbers specified by MIDI. The unless clausechecks to see if the current key number of the background is within the legallimits and, if not, omits that event from the score. We now listen to the octavesand background material:

Interaction 22-4. Listening to enhancement 1.

cm> (events (append (D&eacute;sordre-w/octaves white-mode (make-white-fg-notes 'b3) (make-white-fg-rhythms)) (D&eacute;sordre-w/octaves black-mode (make-black-fg-notes 'ds3) (make-black-fg-rhythms))) "ligeti.mid")"ligeti-4.mid"cm>

→ ligeti-4.mid

Enhancement 2

As our final enhancement we implement processes that add more realism bysimulating the foreground chords that occur in section three:

Example 22-8. Process with foreground chords.

(define (D&eacute;sordre-voices mode ntes rhys chord voimap fgchan bgchan) (let ((moct (scale-divisions mode)) (fg-time 0) (fg-8ths 0) (mode-deg 0)) (list ;; foreground process (process with rhy and voi and key and voices = (new transposer :of (new heap :of chord :for (pval voi)) :on (pval mode-deg)) for count from 0 set fg-time = (now) set mode-deg = (next ntes) set key = (keynum mode-deg :from mode) set fg-8ths = (next rhys) set rhy = (eighth-time fg-8ths) set voi = (lookup count voimap)

Page 278: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

output (new midi :time (now) :channel fgchan :keynum key :duration rhy :amplitude fg-amp) wait rhy if (equal? voi true) ;; add lower octave ... output (new midi :time (now) :channel fgchan :keynum (- key 12) :duration rhy :amplitude fg-amp) else ;; ... else add cluster each k in (next voices true) output (new midi :channel fgchan :time (now) :keynum (keynum k :from mode) :duration (- rhy .01)) until (eop? rhys)) ;; background process. (let ((notes (new range :from (pval (- mode-deg moct)) :stepping (new random :of '((1 :weight 3) 2 (3 :weight .5 :max 1))) :for (pval fg-8ths)))) (process for i below 1064 for k = (keynum (next notes) :from mode) unless (or (= (now) fg-time) (not (<= 0 k 127))) output (new midi :channel bgchan :time (now) :keynum k :duration (- eighth-pulse .01) :amplitude bg-amp) wait eighth-pulse)))))

As in the first enhancement (Désordre-w/octaves) our second enhancementcreates a foreground and background process. The background process isessentially unchanged from the first enhancement and will not be discussedhere. However, the foreground process is considerably more complex in ournew definition because it now generates both octaves and clusters, or randomchords, of notes. The intervallic content of these random clusters is specified to

the process by the chord parameter. The value of chords is a list of intervals

Page 279: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

that the voices random pattern will transpose to the foreground's current modedegree. The density, or number of notes, in each random cluster is controlled bya “voice map” specified in the voimap parameter. The voice map is a list thatcontains pairs of values. The first value in each pair specifies a beat number inthe score (calculated in eighth notes) and the second value specifies thenumber notes that each cluster should contain from that beat forward until thenext pair in the beat map. Since our foreground process will produce bothoctaves and chords, a boolean true value in the voice map will be interpretedas meaning that octaves instead of random clusters should be output. Forexample the beat map:

(0 3 10 #t 15 6)

would specify 3-voice clusters starting at beat 0, octaves starting at beat 10and 6-voice clusters staring at beat 15. The function lookup is used to look upthe current voice number voi in voimap. lookup is similar to interpl in that it

processes lists of pairs whose x value is monotonically increasing. Howeverlookup does not perform linear interpolation, it simply returns whatever y value

is in the list. Since no interpolation is performed the y value does not even haveto be numeric.

We now listen to our final version. Foreground and background processesfor both parts are given their own MIDI channels so that each individualprocesses can heard in isolation or in combination with other specific voices.

Interaction 22-5. Listening to enhancement 2.

cm> (events (append (D&eacute;sordre-voices white-mode (make-white-fg-notes 'b4) (make-white-fg-rhythms) '(-1 -2 -3 -4 -5 -6) '(0 #t 261 1 284 2 297 3) 0 1) (D&eacute;sordre-voices black-mode (make-black-fg-notes 'ds4) (make-black-fg-rhythms) '(-1 -2 -3 -4) '(0 #t 254 1 286 2) 2 3)) "ligeti.mid")"ligeti-5.mid"cm>

→ ligeti-5.mid (four channels)

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file ligeti.cm located in the same directory as the HTML file for thischapter. The source file can be edited in a text editor or evaluated inside theCommon Music application.

Page 280: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003

References

NIL (NIL). Gyorgy Ligeti: decision and automatism in Desordre 1er Etude,Premier Livre. Interface, 1991, ????.NIL (????). The Pattern Meccanico Compositions of Gyorgy Ligeti.Perspectives of New Music, ????, ????.

Previous Contents Index Next

© 2003 Swets Zeitlinger Publishing

Page 281: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index Next

23Spectral Composition

In spectral composition the acoustic properties of sound constitute sourcematerial for music composition. The term spectral refers to the timbral contentof sound — the precise mixture of partials (frequencies, amplitudes and initialphases) that make up a sound's waveform (see Chapters 9 and 13 for adiscussion of sound and harmonics). Spectral composition is also a label thatrefers to a group of contemporary French composers (Dufourt, Grisey, Murail,Risset) that work extensively with timbral information. An overview of some oftheir compositional techniques can be found in An Introduction to the PitchOrganization of French Spectral Music (Rose) by Francois Rose. Many othercontemporary composers also work extensively with spectral information aswell, including John Chowning, Barry Truax and James Dashow. Spectralisttendencies are clearly observable in the work of earlier twentieth centurycomposers such as Andre Jolivet, Olivier Messiaen, Iannis Xenakis, EdgardVarese and Ben Johnston.

Because spectral techniques are based on the physical properties ofsound, computer analysis and synthesis figure strongly in this type ofcomposition. There are two broad approaches to composing with spectralinformation. The first method employs an analysis/resynthesis strategy — aspecific source waveform is analyzed by computer for its harmonic content andthis spectral information is then “resynthesized” back into the composition inways the composer works out. The second approach to spectral composition

utilizes sound synthesis algorithms to synthesize spectral information directly.We will introduce both strategies in this chapter.

Spectral Analysis

Spectral analysis means to deconstruct a sound (a source recording or real-time audio signal) into its constituent partials. Analysis methods vary accordingwhether the waveform is periodic or aperiodic in nature. One particular method,

called the Fast Fourier Transform (FFT) lies at the heart of many computerprograms that analyze complex periodic waveforms. The Frenchmathematician Jean Fourier (1768-1830) discovered that any periodic wavecan be described as a sum of individual sine waves, where each sine wave isdescribed by its frequency, amplitude and initial phase, or starting point. Thissuggests that there are two different ways to “look at” a sound: as a single

pressure wave, or as a collection of individual partials. A time domainrepresentation of a waveform displays the aggregated wave's changing

Page 282: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

amplitude (pressure) over time. A frequency domain representation plots thechanging amplitudes of the individual partials over frequency (Figure 23-1).

Figure 23-1. Time and frequency domain representations

of a 220 Hz Clarinet tone.

→ clarinet.mp3 Clarinet playing A3 (220 Hz).

The FFT algorithm provides an efficient bridge from the time domain to thefrequency domain. An FFT analysis proceeds by first “chopping up” the inputsignal into short time segments called windows. By performing the analysis overa series of windows an FFT program can capture the dynamic characteristicsof a spectrum as it changes over time. Each window in the analysis defines a“snapshot” of the spectrum to be analyzed. The mechanics of the FFT analysiswithin a single window are quite complex. Suffice it to say that the FFT acts likea bank of filters — it measures the amount of energy in frequency “bins” that areequally distributed over the entire range of frequencies. This means that thebins of a given FFT analysis should match, or “line up”, with the spectralcomponents in the input signal. Most analysis programs provide ways to tunethe FFT to the specific sound being analyzed. The output from an FFT analysis

are frames of data that contain the amplitude and initial phase values of eachfrequency bin. This information can then be displayed, resynthesized, or savedto data files.

The important thing to grasp here is that — while the time and frequencydomains are really just different perspectives on the same thing — thefrequency domain representation is particularly useful to the spectral composerbecause it deconstructs the wave into its discrete components that can then beused to inform compositional decision making. For example, one can see quiteclearly from the frequency domain plot in Figure 23-1 that odd numberedharmonics contain much more spectral energy than do the even harmonics.This acoustic information could be reflected, for example, by the composer'spreference for using the proportions 1:3:5:7 to determine critical aspects of thecomposition.

Some FFT programs allow the results from the sound analysis to beexported as data files containing the contents from each frame of the analysis.Example (Example 23-1) shows what data from one frame might look like.

Example 23-1. Spectral snapshot of Clarinet tone in Figure1.

Page 283: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(define clarinet '(1.00 .0842 2.01 .0017 3.00 .0420 4.01 .0029 5.00 .0251 6.02 .0041 6.99 .0115 7.96 .0006 8.99 .0032 9.98 .0003 11.00 .0018 11.96 .0003 12.96 .0006 14.00 .0001 14.97 .0001 15.97 .0005 17.95 .0002 18.96 .0001 19.07 .0001 22.06 .0001))

Example 23-1 displays the spectral information output by the FFT analysis ofthe clarinet tone in shown Figure 23-1. The information contains the first twentyharmonics from the analysis. The data are interpreted pairwise: the first numberin each pair is the frequency ratio of a harmonic and the second number is itsamplitude. Phase information is not represented here. The example was takenfrom the file "spectra.clm" from the Common Lisp Music source distribution.This file contains a large number of spectral sets that can be used ascompositional source material without having to actually perform an FFTanalysis yourself.

Spectral Resynthesis

Output from an FFT analysis is simply data. As such it can be mapped,manipulated and processed in various ways to resynthesize the informationback into a composition. The nature of this transformation is, of course, up toeach composer's imagination (see Chapter 16 for a general discussion ofmapping). In Example 23-2 we define a simple set of tools that allow spectralinformation to be reshaped into different tonal gestures.

Example 23-2. Spectral processing tools.

(define (spectrum-maxfreq spec) ;; return freq of last harmonic in spec (list-ref spec (- (length spec) 2)))

(define (spectrum-maxamp spec) ;; iterate successive tails of spec and find ;; the max amplitude (loop for tail = spec then (rest (rest tail)) until (null? tail) maximize (second tail)))

(define (spectrum-freqs spec) ;; return only the freq components of spectrum (loop for tail = spec then (rest (rest tail)) until (null? tail) collect (car tail)))

(define (spectrum-length spec) ;; return number of partials in spec (/ (length spec) 2))

The spectrum-maxfreq function returns the highest harmonic scaler in spec, alist of harmonic and amplitude pairs such as the one shown in Example 23-1.

Page 284: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Since the harmonic pairs in spec are ordered lowest to highest the functionsimply returns the second-to-last element in the list, which is the frequency valueof the highest harmonic. The spectrum-maxamp function returns the loudestamplitude in spec. Since there is no way to predict which harmonic in aspectrum will be the loudest the function must search the entire spectral list for

the maximum amplitude. The tail variable holds successively shorter versions of

spec. On the first iteration tail is equal to spec but each iteration thereafter sets

tail to (rest (rest tail)) — the remainder of the list beginning at the next

pair of elements to the right. As tail moves successively rightward though spec,the form (second tail) always accesses the amplitude value of the current

pair at the front of the list. Iteration continues until tail is empty and the predicatenull? returns true, at which point the until clause stops the iteration. The

spectrum-freqs function iterates spec in a manner identical to spectrum-maxamp but collects (first tail) — the frequency component of the current

pair in tail, thus reducing the spectrum to a list of its frequency values.Given this basic set of tools we can write a general mapping function to

transform spectral information into different shapes:

Example 23-3. Rescaling spectral data.

(define (rescale-spectrum spec a1 a2 k1 k2) ;; rescale spectrum to lie between keynums k1 and k2 ;; and amplitudes a1 to a2 (let* ((fund (if k1 (* (hertz k1) (first spec)) 1.0)) (key1 (keynum fund :hz)) (key2 (keynum (* fund (spectrum-maxfreq spec)) :hz)) (soft 0) (loud (spectrum-maxamp spec))) (loop for tail = spec then (rest (rest tail)) until (null? tail) collect (list (rescale (keynum (* fund (first tail)) :hz) key1 key2 (or k1 key1) (or k2 key2)) (rescale (second tail) soft loud a1 a2)))))

The rescale-spectrum function rescales a spectrum such that it liesproportionally between the specified key numbers k1 and k2 and the amplitude

values a1 and a2. If k1 or k2 is false their values default to the lowest and

highest frequency values in spec. If k2 is less than k1 then the spectrum will beinverted. The spectrum returned by rescale-spectrum is formatted as a list oflists rather than as list of paired values. Reformatting the spectral data allowseach partial in the spectrum to be represented explicitly as a sublist containinga frequency and an amplitude value. This reduces the total length of thespectrum by half, and allows each partial to be processed as a single unit ofstructure. Given this simple spectral toolkit we can now define a musicalprocess that creates tonal gestures from spectral information. The mainprocess spectral-zither creates zither-like material from the clarinetspectrum using two “helper” processes. The first helper plays an upward ordownward strum of a rescaled spectrum and the second produces random

Page 285: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

plucks of the same material:

Example 23-4. Spectral gestures.

(define (strum-it play rate dur amp up?) ;; play ascending or descending spectrum (process for s in (if up? play (reverse play)) output (new midi :time (now) :keynum (first s) :amplitude amp :duration (vary dur .1)) wait rate))

(define (pluck-it play rate dur amp len) ;; play spectrum in random order (let ((pat (new heap :of play))) (process while (< (now) len) for s = (next pat) output (new midi :time (now) :keynum (first s) :amplitude (interp (now) 0 amp len (/ amp 2)) :duration (odds .2 (* dur 4) (vary dur .1))) wait rate)))

The main process spectral-zither takes a spectrum list (such as the clarinetdefined earlier in this chapter) and generates a self-similar series of spectra inwhich each rescaled version is transposed to a specific harmonic level in theoriginal spectrum. The amplitudes of each partial in the original determines theproportional length of each gesture — the louder the partial in the originalspectrum the longer the transposed gesture lasts. Each gesture consists of twoparts, a loud initial strum followed by random plucking of the same material. Theamplitudes of the random plucks diminish over the length of each gesture.

Example 23-5. The spectral zither process.

(define (spectral-zither spectrum key1 key2 tim1 tim2) ;; transpose tonal gestures to successively higher ;; positions in spectrum. duration of each gesture is ;; proportional to amplitude to the partial it is ;; based on (let ((spec (rescale-spectrum spectrum key1 key2 tim1 tim2))) (process for x in spec for k = (first x) for a = (second x) for topk = (between 100 113) for play = (rescale-spectrum spectrum k topk .4 .9) for up? = true then (odds .5) sprout (strum-it play .075 2.3 .9 up?) sprout (pluck-it play (vary .075 .2) .6 .9 a) wait a)))

Page 286: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

We now generate both upward and downward versions of the spectral zither.The second version uses channel tuning to approximate the spectralmicrotones.

Interaction 23-1. Playing the zither.

cm> (events (spectral-zither clarinet 10 80 2 6) "zither.mid")"zither-1.mid"cm> (events (spectral-zither clarinet 100 20 4 3) "zither.mid" :channel-tuning 9)"zither-2.mid"cm>

→ zither-1.mid→ zither-2.mid 8 channels, microtones. clearbends.mid Clear pitch bends.

1706°F

To conclude this section on analysis and resynthesis methods we examine aspectral composition that was composed with information derived from the FFT

analysis of a tam-tam tone. The piece is 1706°F, for chamber ensemble andelectronic sounds, written by the composer Michael Klingbeil. The analysis datafor the composition was first organized in Common Music and thenmanipulated by the composer to create the instrumental parts and syntheticsounds events (samples) which are triggered at appropriate times during theperformance of the piece.

The original tam-tam sound was approximately 11 seconds long whichresulted in over 300 FFT windows, each corresponding to about 35milliseconds of actual sound. Rather than try to recreate the tam-tam sound withthe instrumental sounds, the composer chose to conceive of each analysiswindow as a kind of “chord”.

Figure 23-2. Plot of the first 30 analysis windows where the

horizontal axis corresponds to window number.

Page 287: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[img/1706Plot.png]

Common Music was used to manipulate the individual chords, first by findingcommon tones between each successive chord and then by selecting chordsbased on the number of non-common tones with its predecessor. Theseselected chords are highlighted in the preceding graphic. The remainingpitches, shown in light gray, became a structural background layer which wasused in part as a basis for the electronic sounds. The melodic and contrapuntalcontours of the instrumental parts were then created by choosing from thereservoir of pitches available in each chord. The compositional method was notalways strictly algorithmic: at times pitches were selected from a larger corpus(such as all of the pitches found in a particular time window or all the pitchessounding within a particular time region spanning across several windows).

Figure 23-3. Musical notation of 8 chords selected from the

first 9 analysis windows. (Note that the pitches may differ

slightly from the graph shown above since some chords

were selected using a different method of quantizingfrequencies to tempered pitches.)

Page 288: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[img/1706ChordMap.png]

Selected chords were translated into common practice notation (with quartertones) to facilitate the free composition of the instrumental parts. In some casesa chord may correspond to only a few seconds of composed music, or in otherinstances as many as 30 or 40 seconds.

Simple processes are used to generate the electronic sounds. Thefollowing is a MIDI simulation of electronic events numbered 40 and 41 in thescore. Event 40 simply sounds the pitches of time window 5 in an ascendingarpeggio. The pitches are doubled at the octave, the octave plus a fifth, and attwo octaves (this is to better simulate the electronic sounds which are actuallyused in the piece). Event 41 is a chord which sounds all pitches of analysiswindow 6 simultaneously.

In this example a portion of the data used in the piece is bound to the

variable tam-tam-data. Each element of the list corresponds to the spectrum ofa single analysis window (the first five windows are empty for this example).

Example 23-6. The tam-tam FFT data.

(define tam-tam-data '(() () () () () (156.77036815728735 90.637619 259.9496481697027 82.718292 286.574546692396 100.874054 306.25563026238274 93.336433 324.7524389568296 100.055481 337.44764076690535 102.149223 364.6634271878465 230.778137 415.7531705386655 119.252495 606.0946243520343 86.700752 693.632505316461 99.007416 793.6624934018535 122.259544 884.1881093270954 157.20726 933.8675278134929 145.184113 1088.9671367780802 89.725365 1488.6904580535806 101.671417 1511.527861059107 104.7155 1618.8507000903057 84.252243 1713.9968634651475 112.454514 1765.292105410349 89.296089 1890.2215478184416 99.125633 1908.0848686296388 92.404488 1999.0901910667542 99.439217 2041.0190392835411 105.710556 2179.3234227680086 88.835609) (156.9226656365204 84.869324 260.82045952862836 90.014053 285.1896751834164 111.457977 307.17019597206075 99.64637 336.09609235859693 120.298607 362.75665648480026 225.252136 415.73593078208086 115.339218 655.5235086291844 87.095612 692.9686610934529 82.759056 793.4209536257428 108.587471 850.8079688652781 95.086945 883.5759843738408 150.945786 932.8432789368185 150.02179 1088.3966109501614 85.69706 1490.71509379789 112.47464 1512.3888337014348 92.084824 1619.7171692738077 82.596962 1712.6721745280083 102.734337 1890.1368493016446 97.024925 1908.251766584189 94.600105 1998.8849421756013 106.950005 2009.4111265591894 83.189438 2041.6380351691066 102.523079 2402.793452296438 84.39566)))

Page 289: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

As with the clarinet data, each window in the tam-tam data is organized asfrequency and amplitude pairs. In the case of the tam-tam data the frequencyfor each harmonic is recorded in Hertz rather than in scaler form. Given ourspectral tool kit and this tam-tam data we can define two sample chords fromthe piece:

Example 23-7. Two sample chord events.

(define (event40) (let ((data (rescale-spectrum (list-ref tam-tam-data 5) 0.5 1.0 #f #f))) (process for delta in (append (explsegs 3 0.5 4) (explsegs 4 0.6 1/2) (explsegs (- (length data) 7) 2 1/8)) for s in data for count from 0 each kt in '(0 12 19 24) output (new midi :time (now) :keynum (+ (first s) kt) :amplitude (second s) :duration 1.5) wait delta)))

(define (event41) (let ((data (rescale-spectrum (list-ref tam-tam-data 6) 0.7 1.0 #f #f))) (process for s in data output (new midi :time (now) :keynum (first s) :amplitude (second s) :duration (between 8.0 10.0)) wait 0)))

We now generate both chords together but sligtly offset in time.

Interaction 23-2. Generating sample chords from 1706F.

cm> (events (list (event40) (event41)) "1706Example.mid" '(0 3.75))"1706Example.mid"cm>

→ 1706Example.mid

In the actual piece each pitch of the arpeggio is composed of a simplesynthetic tone and a snippet of a tam-tam sound which is comb filtered to soundat the desired pitch. The file 1706Event40.mp3 contains the synthesized soundfor Event 40. The chord of event 41 consists of noise filtered with very sharpfilters at each frequency of the chord can be heard in the file 1706Event41.mp3.

Page 290: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The upper pitches are doubled with a sampled crotale sound and a snippet of acymbal sound is mixed in. Figure 23-4 shows the electronic part as it looks inthe score.

Figure 23-4. This excerpt shows the combination of

electronic events with the instrumental parts.

[img/1706ScoreExample.png]

→ 1706Example.mp3 Passage in performance.

Spectral Synthesis

An alternate way to work with spectral information is to directly compute it using

synthesis algorithms. A synthesis algorithm is a software program thatcalculates a waveform. As a very simple example of this process we will

synthesize a spectrum consisting of the combination tones of two sine waves.

When two sine waves f1 and f2 sound together, nonlinearity in the frequencyresponse of our ears causes us to perceive frequency components above andbelow the sine tones. Sum tones are tones that lie at f1+f2 Hz and difference

tones lie at f1-f2 Hz. Since sum tones are above the generating sine wavesthey tend to be masked but the difference tones can be clearly heard. The filediff.mp3. contains four difference tones, each difference tone is first“previewed” and then generated by pairs of sinewaves.

Page 291: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Example 23-8. Computing sum and difference notes.

(define (combo-notes n1 n2) (if (scale= n1 n2) '() (let ((f1 (hertz n1 )) (f2 (hertz n2))) (list (note (abs (- f1 f2)) :hz) (note (+ f1 f2) :hz)))))

The combo-notes function synthesizes combination notes by adding andsubtracting two notes. If both notes are equivalent in the standard scale then theempty list is returned, otherwise the difference and sum tones in Hertz iscalculated and the nearest tempered notes are returned in a list. Since Hertz

values are added and subtracted neither input n1 or n2 will appear in the outputspectrum and the synthesized components will usually be inharmonically relatedto the original tones.

Interaction 23-3. Calculating sum and difference sets.

cm> (combo-notes 'd5 'e5)(d2 ef6)cm> (combo-notes 'a4 'ds5)(fs3 c6)cm>

In the audio domain this technique is referred to as ring modulation and the

sum and difference tones are called spectral side bands. Ring modulation canbe performed on individual pairs of frequencies (sine waves) or on complexwaves. In the latter case, each frequency in the first wave combines with eachfrequency in the second to form an output spectrum that represents the “crossproduct” of the two inputs. We can implement compositional ring modulationusing loop to append results returned by combo-notes:

Example 23-9. Synthesizing note sets by ring modulation.

(define (ringmod set1 set2) (scale-order (loop for n1 in set1 append (loop for n2 in set2 append (combo-notes n1 n2)))))

(define (play-rings notes rate) (process repeat 25 for k = 70 then (drunk k 3) for r = (ringmod (list k) notes) for l = (length r) output (new midi :time (now) :duration (* rate l) :keynum k :amplitude .6)

Page 292: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

each s in r as i from 1 output (new midi :time (+ (now) (* rate i)) :keynum s :amplitude .3) wait (* rate l)))

The ringmod function returns the spectral cross product of two input note sets.The Common Music function scale-order ensures that the notes in thesynthesized spectrum are ordered low to high. See Chapter 9 for a discussionof scale-order.

Interaction 23-4. Ring modulated melodic line.

cm> (events (play-rings '(c4 e4 g4) .1) "rings.mid")"rings-1.mid"cm>

→ rings-1.mid

Frequency Modulation

In the remainder of this chapter we will examine Frequency Modulation (FM)as a way to synthesize tonal material for composition. FM audio synthesis wasdiscovered by my teacher John Chowning at CCRMA, Stanford University.Chowning's FM paper The Synthesis of Complex Audio Spectra by Means ofFrequency Modulation (Chowning) is a seminal paper in Computer Music.Good non-technical discussions of FM audio synthesis are available inComputer Music (Dodge) and in Road's The Computer Music Tutorial (Roads). William Schottstaedt's An Introduction to FM (Schottstaedt-fm) included in theCLM source distribution discusses the theory behind FM and also shows thereader how to design FM instruments in Common Lisp Music. Schottstaedt'sFM instrument fm-violin is briefly discussed in Chapter 24. A number ofcomposers, including John Chowning, have used FM as a compositionaltechnique as well as for audio synthesis. FM is a particularly powerful synthesisalgorithm because it can describe many different types of spectra using only afew simple control parameters.

Anyone who has listened to a good violinist can understand the basic ideabehind frequency modulation. One of the ways a trained violinist adds warmth

and expression to sound is by adding a small amount of vibrato to the tone byrocking the string finger back and forth in a constant manner. When the tip ofthe string finger angles toward the violinist the string is shorted by a very smallamount and the frequency of the tone rises slightly; when the finger rocks backin the other direction the string is lengthened and a slight drop in pitch isproduced. The constantly changing string length has the effect of modulating, oraltering, the frequency of the main tone by a small percentage on either side ofits center frequency. Vibrato is a periodic oscillation with two components: arate (frequency) and an amplitude. The rate of oscillation is how fast the violinist

Page 293: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

moves the finger back and forth. This can be measured in Hertz, or cycles persecond. The amplitude of the vibrato determines how much the main tonedeviates around its center frequency. In this case the term “amplitude” is really

describing an amount of change of frequency, or deviation. Typical values forvibrato frequency lie around six Hertz with a deviation of about 20 cents,approximately 1 percent around the center tone. The effects of changing therate and deviation of a periodic vibrato can be heard in vib-1.mp3. Theexample demonstrates vibrato rates of 2, 4 and 8 Hz with deviations of 1/2. 1and 2 percent around a 440 Hz tone.

FM Synthesis

What we have described so far is a system in which one frequency, called the

carrier, is slightly modulated by another, called the modulator Now imaginereplacing our violinist with a robot capable of producing vibrato at extreme(audio) rates and with very wide deviations. As the rate of vibrato increasesand its deviation becomes wider about the center frequency, the carrierbecomes distorted to such an extent and so quickly that modulation is no longer

perceived as a changing central pitch but as a changing timbre. “Robot” vibratorates of 20, 40 and 80 Hz with deviations of 10, 25, and 50 percent around a440 Hz tone can be head in vib-2.mp3 This is FM synthesis. FM synthesis

generates a complex waveform by modulating, or changing, one wave calledthe carrier by another wave, called the modulator, at audio rates. Thismodulation causes spectral sidebands to appear at multiples of the modulatorabove and below the carrier:

carrier ± (k * modulator)

where k is the order of the sideband, For example, a carrier of 400 Hz and amodulator of 100 Hz produces sidebands at the frequencies shown in Table23-1.

Table 23-1. Sidebands arrangements for a carrier of 400

Hz and modulator of 100 Hz.

c-3m c-2m c-1m c c+1m c+2m c+3m

100 200 300 400 500 600 700

Note that at some value of k the lower sideband will become a negativefrequency component. A negative frequency can be understood as a positivefrequency with its phase inverted. This means that negative components can be“wrapped around”, or reflected, into the positive domain by inverting theiramplitudes and summing with any existing sideband located at the samepositive spectral position (Figure 23-5).

Figure 23-5. Sideband arrangements for a carrier of 400

Hz and a modulator of 400 Hz. The first graph contains thenegative frequency components, the second shows them

Page 294: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

inverted in the positive frequency domain.

→ fm1.mp3 Carrier=400Hz, C:M=1:1, Index=4.

For a given carrier frequency c and modulator m the c:m ratio determines theprecise arrangement of the spectral sidebands. The c:m ratio of 1:1 shown inFigure 23-5 produces sidebands at integer multiples of the carrier frequency —in this instance the FM spectrum mimics the harmonic series with a“fundamental” at the carrier frequency. The 1:1 ratio is the only c:m ratio whichproduces all harmonics with the carrier at the fundamental. All other ratios eitherproduce a fundamental different from the carrier, or do not generate allharmonics, or generate sidebands that are not multiples of the carrier and soproduce inharmonic spectra (Table 23-2).

Table 23-2. C:M sideband arrangements for a carrier of400 Hz.

c:m sidebands result

1:2 400 1200 2000 2800 3600 Odd harmonics.

4:1 100 200 300 400 500 600fundamental notcarrier.

1:pi400 857 1657 2113 29133370

inharmonic spectrum

We can infer a few facts from Table 23-2:

For the carrier frequency to be considered the fundamental, the c:m ratiomust be 1:1 or m must be equal or greater than two times c.For ratios like 1:m where m is an integer greater than 1 the mth harmonicis missing from the spectrum.If c or m are not integers then inharmonic spectra result.

Figure 23-6 shows the sideband arrangements for a c:m ration of 1:Phi (theGolden Mean, or approximately 1.618): Notice that when negative inharmoniccomponents wrap around to the positive domain they produce a “denser”spectrum because sidebands do not line up in the fashion seen in Figure 23-3.

Figure 23-6. Inharmonic spectra for a carrier of 500 Hz, c:m

Page 295: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

ratio of 1:phi and an FM index of 4.

→ fm2.mp3 Carrier=400Hz, C:M=1:phi, Index=4.

For more information on C:M ratios and their effect on FM spectra see BarryTruax's Organizational Techniques for C:M Ratios in Frequency Modulation.(Truax)

The precise relationship of sidebands to the carrier frequency depend notonly on the rate of modulation but also the amount of deviation. Recall thatdeviation is the amplitude of the modulation — the maximum depth (in Hertz)that the modulation reaches on either side of the carrier. Deviation determinesthe number and amplitudes of the spectral components. Deviation is typically

expressed indirectly, using an FM index that relates deviation to the modulationfrequency:

deviation=index*modulator

If the FM index is zero then there is no deviation and only the carrier wave isheard. But as the index increases from zero an increasing amount of energy is“stolen” from the carrier and distributed over an expanding number ofsidebands. (Figure 23-7) The precise amplitude relationships between thesidebands, carrier and FM index is determined by Bessel functions thatdescribe what the amplitudes are in each sideband order for a given index.Bessel functions look a bit like dampened sinusoids, and can produce negativeamplitude values. Sidebands with negative amplitudes are interpreted ashaving a positive amplitude but inverted phase.

In general, the number of sidebands k produced on either side of the carrierwill equal the closest integer to the FM Index plus 1. Since there are twospectral components per side band, and the spectrum also contains the carrier,the total “density” of the spectrum will be approximately 2k+1. For example, anindex of 2.5 will produce nine components: four side bands (eight spectralcomponents) plus the carrier. However, since negative frequencies wraparound they may combine or cancel existing positive frequency components sothe resulting spectrum many actually have fewer components.

Composing with Frequency Modulation

Given a basic understanding of Frequency Modulation we can begin to workwith FM synthesis as a compositional tool for computing both harmonic andinharmonic sets given three simple control values:

Page 296: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

a carrier frequency, the central tone for the note set.

a modulation ratio that determines harmonicity of the resulting spectrum.an fm index that controls the number of sidebands, or the density of thespectrum.

(fm-spectrum carrier mratio index [spectrum] [:unique b] [:invertb])

The fm-spectrum function returns the FM sidebands (frequencies and optionally

amplitude values) calculated for a specified carrier, mratio and index. Thetypes of values returned in the spectrum are determined by the value of the

spectrum argument. If spectrum is :raw, then the both frequency and amplitudevalues are returned for each sideband and the spectrum may include negativefrequencies. If spectrum is :hertz (the default) then negative sidebands arereflected into the positive frequency domain with their phases inverted. If:spectrum is :keynum or :note then sidebands are returned as key numbers orrounded to their nearest note names. The value of :spectrum can also be a list

like (c4 c6) or (48 80) in which case the two values specify boundaries withinwhich the spectral components will be fit regardless of which octaves thesidebands were actually located in. If note names are specified as boundariesthen the spectrum is returned as notes otherwise key numbers are returned. Ineither case, the value of the :unique argument is used to determine if separatesidebands that produce identical note values are reduced to a single entry ornot. The default value is true. If the :invert keyword is true then the spectrum isinverted before it is returned. If :unique is true then no duplicate key numbers ornotes are returned for partials that map to the same spectral positions.

Interaction 23-5. Generating note sets.

cm> (fm-spectrum 220 1 1)(220.0 440.0 660.0)cm> (fm-spectrum 220 1 5 :note)(a3 a4 e5 a5 cs6 e6 g6)cm> (fm-spectrum (hertz 'c3) 2 5 :note)(c3 g4 e5 bf5 d6 fs6 fs6 af6)cm> (fm-spectrum 220 1 2 :keynum :invert t)(33.0 37.980453 45.0 57.0)cm> (fm-spectrum 220 1 9 '(a3 a5))(c4 d4 e4 fs4 g4 bf4 c5)cm> (fm-spectrum (hertz 'c3) 1.618 6 '(c4 g5) )(c4 d4 e4 f4 g4 bf4 b4 cs5 d5 e5 f5 g5)cm>

The following example uses fm-spectrum to generate a piano “improvisation”.

Example 23-10. Playing FM spectra.

(define (fm-harmony note ) (let ((mrat (between 1.0 2.0)) (indx 1)) (fm-spectrum (hertz note) mrat indx (list (transpose note -12)

Page 297: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(transpose note 12)))))

(define (fm-piano ) (let* ((rhy (new random :of ((q weight 2) (tq :min 3) (,(new cycle :of '(e q e))))))) (process for n = 60 then (drunk n 3 :low 40 :high 80 :mode :jump) repeat 40 for s = (fm-harmony (note n)) for r = (rhythm (next rhy) 70) do (print (list (note n) s)) output (new midi :time (now) :keynum n :amplitude .75 :duration (* r .9)) output (new midi :time (+ (now) (rhythm 'e 70)) :keynum (list-ref s (pick 0 (1- (length s)))) :amplitude .7 :duration (* r .9)) if (odds .6) each k in s output (new midi :time (now) :keynum k :duration (* r .9) :amplitude (between .4 .55)) wait r )))

The fm-harmony function returns an FM note set for a specified center note. The

modulation ratio mrat is randomly selected between 1.0 (harmonic series) lessthan 2.0, so inharmonic spectra generally result. The index 1 produces 2 sidebands on either side of the carrier for a total of 5 notes in each spectrum. Butsince the spectrum argument is specified as octave boundaries on either sideof the carrier, fewer notes will be returned if transposed sidebands produce thesame value inside the boundaries.

The fm-piano process plays 50 spectra generated by fm-harmony. On eachiteration the process plays the center tone and either the lowest or highest notein the spectrum. Sixty percent of the time the process also adds the entirespectrum as a chord.

Interaction 23-6. Listening to fm-piano.

cm> (events (fm-piano ) "fm-piano.mid")"fm-piano-1.mid"cm>

→ fm-piano-1.mid

The Aeolian Harp

Page 298: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

We conclude this chapter with two examples of FM tonal synthesis in TheAeolian Harp, a composition for piano and computer generated tape written bythe author. Links to the full score, program notes and a recording are includedat the end of this section. All the sounds in this 22 minute composition havetheir basis in either FM synthesis or in the justly tuned Aeolian scale. The tapemusic for Aeolian Harp consists of real sounds (primarily wind and pluckedpiano strings) that have been transformed or colored in some mannerappropriate to the composition. These techniques include resonating combfilters, time stretching, and cross synthesis. Cross synthesis was used to createthe “singing wind” that occurs toward the end of the composition as windsounds take on the spectral properties of the Buddhist chant Zen Sho Kadasung by the Rev. Haruyoshi Kusada of Berkeley, California. A short passagefrom the seventh Visions Fugitive by Serge Prokofieff (title Arpa) appearsbriefly with Zen Sho Kada. The tape part was created using Common LispMusic, a sound synthesis language developed by William Schottstaedt atCCRMA, Stanford University. All of the melodic and harmonic structures in thepiano part were derived through Frequency Modulation. Many of the pianosections follow a similar technique: basic carrier, mratio, and FM index valuesare first determined using envelopes or patterns and then varied by some slightrandom amount to produce fresh spectra on each output set. The first example(Example 23-11) comes from the “bell section” near the beginning of thecomposition. The piano plays a constant A440 pulse with a harmonicaccompaniment generated by FM using a carrier is A440, a mratio and anindex:

Figure 23-7. Center frequency of A4 or A3, mratio between

1.1 and 1.6, and FM index of 2.4.

→ ah1.mp3 Piano and tape, measures 17-22 (page 2 ofscore).

Here is a simplified version of the process that generated the bell-like FMchords.

Example 23-11. FM Bell harmony.

(define (fm-bell-notes tone ) (let ((cen (hertz tone)) (rat (between 1.1 1.6))

Page 299: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(ind 2.4)) (fm-spectrum cen rat ind (list (transpose tone -12) (transpose tone 12)))))

(defun fm-bell (reps rate) (let ((tones (new random :of '((a4 :weight 3) (a3 :max 1))))) (process repeat reps for tone = (next tones) for bell = (fm-bell-notes tone) output (new midi :time (now) :keynum tone :duration 3 :amplitude .6) when (odds .65) each k in bell output (new midi :time (now) :keynum k :duration 3 :amplitude .4) wait rate )))

In Interaction 23-7 we listen to twenty fm-bells.

Interaction 23-7. Listening to fm bells.

cm> (events (fm-bell 20 1.25) "fm-bell.mid")"fm-bell-1.mid"cm>

→ fm-bell-1.mid

The second example from Aeolian Harp demonstrates melodic materialgenerated through FM synthesis. The excerpt comes from the beginning of thelong piano solo in the middle of the piece. The entire solo is generated by FM.Each measure in Figure 23-8 constitutes one FM spectrum. The measures areplayed at a constant rate no matter how may notes are in the spectral set — thelarger the set the faster it must be played but the carrier is always heard as thefirst note of each measure. The FM index in this section was controlled as afunction of time. Over the course of the section the index and note boundariesincrease so that the sets become more jagged and chromatic. Figure 23-9shows what the spectra look like near the end of this section.

Figure 23-8. Carrier A4, A3 or A5, mratio between 1.1 andpi, index between 2 and 2.5.

Page 300: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Figure 23-9. Same process with increased FM index andenlarged note boundaries.

→ ah2.mp3 Piano solo, measures 118-150 of score.

The following process example is a slightly simplified version of the melodicgenerator.

Example 23-12. FM melodic generator.

(define (gen-mel cen rat ind low high) (fm-spectrum (hertz cen) rat ind (list (transpose cen low) (transpose cen high)) :scale-order nil :invert true :unique nil))

(define (fm-melody measure ratlow rathigh indlow indhigh blow bhigh) (let ((starts '(a4 a4 a4 a3 a4 a4 a5 a4))) (process for cen in starts for rat = (between ratlow rathigh) for ind = (between indlow indhigh) for mel = (gen-mel cen rat ind blow bhigh) for rhy = (/ measure (+ 1 (length mel))) output (new midi :time (now) :keynum cen :amplitude .6 :duration .4) each n in mel as r from rhy by rhy output (new midi :time (+ (now) r) :keynum n :amplitude .4 :duration 2) wait measure)))

Page 301: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

The gen-mel function generates an inverted fm spectrum (the “undertones” ofthe carrier) fit between two boundary notes low and high. Since :unique isfalse, the spectrum is not reduced to a set after the spectral components havebeen fit between the boundaries. The fm-melody process plays a series of FMspectra generated by fm-mel as a single melodic line. Each spectrumrepresents one measure of music with a constant duration; the more notes in aspectrum the faster they are be played.

This example approximates the piano in Figure 23-8:

Interaction 23-8. Listening to fm-melody

cm> (events (fm-melody 1.8 1.1 pi 2.0 2.5 -24 12) "fm-melody.mid")"fm-melody-1.mid"cm>

→ fm-melody-1.mid

The score, recording and program notes of the entire composition are locatedin this chapter's directory:

Aeolian Harp ScoreAeolian Harp Recording (recorded on April 3, 2002 at the University ofIllinois; Mei-Fang Lin, pianist.)Aeolian Harp Program Notes

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file spectral.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

References

NIL (???). An Introduction to the Pitch Organization of French Spectral Music.Perspectives of New Music, ???, ???.Chowning, J. (1973). The Synthesis of Complex Audio Spectra by Means ofFrequency Modulation. Journal of the Audio Engineering Society, 21(7), ????.Dodge, C. & Jerse, T. (1985). Computer Music. New York: Schirmer Books.Roads, C. (1996). The Computer Music Tutorial. Cambridge: MIT Press.Schottstaedt, W. (????). Introduction to FM [On-line]. Available http://www-ccrma.stanford.edu/Software/CLM/.Truax, B. (1978). Organizational Techniques for C:M Ratios in FrequencyModulation. Computer Music Journal, NIL, 39-45.

Previous Contents Index Next

Page 302: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

H. Taube 05 Oct 2003© 2003 Swets Zeitlinger Publishing

Page 303: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

Previous Contents Index

24Beyond MIDI

All of the algorithms, examples and Etudes in this book have described musicalsound in terms of MIDI events and MIDI score files. The simplicity of MIDI hasbeen a convenience for us — a way to generate musically satisfying resultswhile maintaining our focus on larger compositional issues and algorithmic

design. Indeed, the MIDI protocol is very convenient: it is simple, universallyavailable on all makes of computers and an industry standard that has beenadopted by most commercial music software applications. But the benefits ofthe MIDI protocol — its simplicity and ease of use — come only at the price oflimiting compositional expressiveness and control over the production of anacoustic signal. We have already encountered some of MIDI's limitations duringthe course of this book. For example, we have seen that MIDI has an extremelyweak representation of frequency, which it defines in terms of only 128 “keynumbers”. The protocol has many other limitations as well, including a very slowdata rate, poor 7-bit quantization, and an extremely limited channel, orinstrument, address space.

But perhaps the most severe restriction that MIDI imposes on a composer

has more to do with its nature than its features: MIDI is a control protocol andnot a language for sound synthesis. From the perspective of a composer usingMIDI, a synthesizer is a “black box” whose hardwired operations are hiddenbehind the messages that MIDI transmits. MIDI makes it very easy to use thesynthesizer but at the price of control over synthesis algorithms. One solution tothis problem is to purchase a number of synthesizers, each with different anddistinct capabilities. It is quite common for composers who work seriously withMIDI to have a whole rack full of expensive synthesizers, samplers and effectsprocessors. But many composers eschew MIDI completely in favor of sound

synthesis languages that permit the specification of audio signals on asample-by-sample basis. These languages generate sound from scoreinformation using audio algorithms, or “digital instruments” that the composerspecifies or creates.

In this chapter we introduce sound synthesis as an alternative to MIDI soundgeneration. We will learn how to work with two different sound synthesislanguages: Csound and Common Lisp Music. Both of these languages providethe composer with a rich set of audio operators and an ability to design audioinstruments from scratch. Since Csound and Common Lisp Music exhibit acertain similarity in their design, which of these languages a composer choosesto work with is largely a matter of preference. Among the factors to considerwhen selecting a synthesis language are: availability on your computer andoperating systems, speed of execution, interactive control, expressive power,

Page 304: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

extensibility of system, and how well the software is supported. Of course it isnot possible in this chapter to explain much about these synthesis languages inparticular — these topics can only be properly addressed in books. ComputerMusic (Dodge) is an excellent introductory textbook that discusses most of thesynthesis techniques in use today. Its presentation can be applied to anysynthesis language you choose to work with.

Sound Synthesis Languages

A sound synthesis language is a software system that defines audio

processing operators called unit generators and a syntax for combining these

operators to form instruments. Each type of unit generator implements adistinct, generic operation on an audio signal. Common unit generators includeoscillators, envelope generators, wave tables, delay lines and various types ofdigital filters. By routing the output of one unit generator into the input of another,a relatively small number of different generator types can be “chained together”in different configuration to implement different synthesis algorithms. Eachcollection, or network, of operators is called a synthesis instrument. Synthesisinstruments are often depicted as a graphs or “patches” in which the differentunit generators are connected by lines representing the signal's path as it flowsfrom generator to generator.

Synthesis runtime environments

Synthesis languages make a distinction between the activity of designing aninstrument and actually using the instrument to create an output audio signal.This latter activity is the responsibility of the synthesis runtime environment.The job of the runtime environment is to route external synthesis data to specificsynthesis instruments executing inside the environment. This routing may be

implemented in or out of real time. Some languages provide a score fileinterface to the synthesis runtime environment. A score file is a text file that

contains a time ordered series of instrument calls, where each call is roughlyanalogous to a note in a traditional music score. An instrument call in a scorefile associates the name, or identifier, of a synthesis instrument with theparameter data that defines one particular sound. The parameter data providethe initial input conditions for the instrument's audio algorithm to generate

sound output. The synthesis data enters the instrument through its parameters,or input variables. Thus the notion of an “instrument call” in a score file is verysimilar to a function call in Lisp, and as we will see shortly, a synthesisinstrument can be implemented as a specialized type of Lisp function.

Instruments and parameters

One of the challenges of working with digital instruments is to learn whichparameters produce the interesting results. Since each parameter adds anadditional dimension of complexity to a digital instrument, composers mayspend hours experimenting with parameter values for an instrument in order tolearn about an instrument's acoustic capabilities. The first task in this

Page 305: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

exploratory process is to understand what each parameter means to a giveninstrument, and what the valid range of values are for each parameter. Sinceeach synthesis instrument implements its own synthesis algorithm, the numberand types of parameters can vary widely from instrument to instrument. It is notuncommon for synthesis instruments to have twenty or more parameters! Aninstrument might require a large number of parameters because it uses largeamounts of input data (as in the case with additive synthesis instruments, forexample), or because the instrument supports many optional features that affectits behavior, for example different parameters for vibrato, glissando,reverberation, and so on. In order to reduce the amount of parameter data in

instrument calls, a language like CLM allows default values to be associatedwith optional parameters. This means that a parameter value must only bespecified in an instrument call if it is actually different from the value theinstrument would otherwise use. Although the order and meaning of instrumentparameters cannot be known without consulting documentation, manyinstruments commonly adhere to a conventional interpretation of the first fewparameters:

name start duration frequency amplitude

where name is the name or identifier of the instrument, start is its start time inthe score in seconds, duration is its duration, frequency is in Hertz and

amplitude is its amplitude value. The following table shows what two instrumentcalls might look like for Csound and Common Lisp Music.

Table 24-1. Example instrument calls in Csound and CLM.

Csound i1 0 3 440 .5

CLM (beep 33.2 10.0 8231.43 .75)

In the remainder of this chapter we will learn how to define new musical objectsand use them in conjunction with Csound and Common Lisp Music. Althoughmusical sound will be rendered quite differently than by using MIDI, it isimportant to understand that — when viewed from the perspective of themetalevel — very little will actually change. The only practical differences will be:

Compositional algorithms will express sound using sound event classesthat permit more explicit control over the syntheses of sound than usingthe MIDI event class.Each new sound event class has its own set of sound parameters,depending on the instrument it uses to render sound.Frequency will be specified using Hertz values rather than key numbers ornote names.MIDI files will be replaced by Csound or CLM score files and audio files.

Csound

Csound is a synthesis language that runs on most computers and operatingsystems in use today. As its name would suggest, Csound is written in the C

Page 306: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

programming language but the actual syntax of the synthesis language is not C.Csound's runtime environment can be controlled by MIDI in “real time” or byinput from score files. To generate sound, Csound combines what it calls an“orchestra” of synthesis instruments with synthesis data in a score files. Boththe orchestra and score are simply text files with information encoded inCsound's specialized instrument and score syntax. (The orchestra file has an“.orc” file extension and a score file has a “.sco” extension). The details ofCsound's instrument syntax is beyond the scope of this book but it is not difficultto understand its general characteristics. We will start with the orchestra filefirst. Each synthesis instrument in an orchestra file is identified by a unique

instr, or instrument number. Example 24-1 show the contents of of an orchestrafile called "blur.orc" that contains a single instrument definition.

Example 24-1. Contents of the Csound orchestra file"blur.orc".

sr = 44100kr = 4410ksmps = 10nchnls = 1

;;;;;; simple oscillator with envelope;;;;;; f1: 0 <size> 10 1 0 1 0 1 0 1 0 1 0 1

instr 1idur = p3iamp = p4*32767 ; scale logical amplitudeifreq = p5iattackt = p6irelt = p7

k1 linen iamp, iattackt, idur, irelta1 oscili k1, ifreq, 1out a1endin

The instrument defined in Example 24-1 consists of a single oscillatorcontrolled by an amplitude envelope. The first few lines of the file set the global

sampling rate sr, control rate kr and number of audio output channels nchnls forsound synthesis. Our instrument's instr number is 1, which means thatinstrument calls that reference this instrument in the score file will use the namei1. The i1 instrument requires 7 parameters; some of these parameters be

seen in the Example 24-1 listed by their p names: (p2, p3, p4 and so on). Table24-2 lists the parameters and pfields of the i1 instrument with example valuesfor each parameter.

Table 24-2. i1 instrument parameters identified by their

pfields.

name time dur amp freq amp- amp-

Page 307: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Macro]

attack release

pfield p1 p2 p3 p4 p5 p6 p7

values i1 0.0 1.0 .5 440 .1 .1

The next five lines in the instrument's definition initialize local variables used bythe instrument. The instrument's duration idur is set to the value of the p3

parameter. The instruments amplitude iamp is calculated by rescaling the

logical amplitude in p4 by 32767, which converts it into Csound's internalamplitude scale. The next three lines proceed in a similar way: the frequency

ifreq in Hertz is set to the value of p5, the attack time of the instrument's

amplitude envelope iattackt is set to p6 and the envelope's decay time irelt isset to p7. The actual instrument patch appears in the next two lines; this is theruntime code that is executed repeatedly to generate the audio output signal. Inthe first line the envelope unit generator linen (line envelope) calculates the

current sample's amplitude value k1, which becomes the amplitude input to aninterpolating oscillator ocili. The oscillator produces a sine wave at the

frequency ifreq and sets the variable a1 to the current audio sample. The outstatement then writes the current sample to the open audio output file.

Defining a Csound event

Given the fact that we can define different instruments in Csound, each with itsown set of parameters and sound characteristics, we must have some meansfor defining musical events that can output these sound descriptions to Csoundscores. More specifically, in order to work with the i1 instrument defined in the"blur.orc" file we must define a new musical event that will output i1 instrumentcalls to a Csound ".sco" file.

The easiest way to create a new type of Csound event is to define it interms of a system defined event class called i. Common Music provides the ievent class so that different Csound instrument events can be created simply by

subclassing this one event class. Recall from Chapter 11 that a subclass

inherits all the slots and values declared by its superclasses. In the case of theCsound i class, its subclasses will automatically receive the following slotinitializations:

:ins integerThe instrument number of the CSound instrument.

:time numberThe start time of the sound event.

:dur numberThe duration of the sound event.

We will use the defobject macro to define our new sound event class.

(defobject name (supers ....) (slots ...) [(:parameters ...)])

The defobject macro defines a new class of object. name is the name of the

new class. Following name comes a list of zero or more superclasses from

Page 308: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

which the new class inherit slots and values. Following the superclass listcomes a list of slot specifications. Each slot in the list is either the name of aslot or a list of the form:

(slot [:initform x])

If an :initform expression is specified its value x becomes the value of slot if noinitialization is provided when the object is created using the new macro.Following the slot specification list comes an optional parameter list:

(:parameters slot ...)

This list declares the names of the slots that will produce parameter values forthe instrument. The order of the slots in this list determines the order the valuesappear in the instrument call written to the score file. Example 24-2 shows thedefinition of a new class of object called i1 that will output sound events for ouri1 instrument:

Example 24-2. Defining a new class of object.

(defobject i1 (i) (amp freq (attack :initform .05) (release :initform .25)) (:parameters time dur amp freq attack release))

This defobject form defines a new sound event called i1 that inherits slots andvalues from the i superclass. The defobject expression declares both slots andparameters for our new object. The amp and freq slots for i1 are declaredwithout specifying initial values, which means that these value will always haveto be specified when an i1 object is used. The attack and release slots, incontrast, are defined with initial values. If an initialization is not provided, thevalues for the attack and release slots will be .50 and .25, respectively.

Following the slot list comes the:parameters declaration list. The time anddur slots that appear in this list were inherited from the i superclass.

Working with the i1 object

Once the defobject expression in Example 24-2 is evaluated we can create i1objects, as shown in Interaction 24-1:

Interaction 24-1. Creating i1 events.

cm> (new i1)#i(i1 ins 1 attack .05 release 0.25)cm> (new i1 :time 0 :freq (random 1000) :amp .5)#i(i1 time 0 ins 1 amp 0.5 freq 693 attack 0.05 release 0.25)cm>

Page 309: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

Generating a Csound score file

Now that the i1 object has been defined we can write a simple exampleprocess that outputs i1 events:

Example 24-3. Generate i1 events.

(define (blur ) (process repeat 100 output (new i1 :time (now) :freq (between 20 2000) ; Hertz :dur .4 :amp .1) wait .015))

At this point we can generate a Csound score. But before doing so we will firstestablish an output hook to automatically call the CSound program with ourorchestra file and score file whenever the events function outputs a CSoundscore. You can define your own Csound hook function or use the play-sco-file hook defined by Common Music:

(play-sco-file file [:orchestra s] [:options s] [:output s] [:play b])

The play-sco-file functions plays the score file using the specified :orchestrafile. :options is an optional string of commands that will be passed to theCSound program when it starts. :output is the output sound file or “dac” ifCsound will generate the audio signal directly to the speakers. By default, theplay-sco-file function assumes that the CSound program can be startedusing the shell command: "/usr/local/bin/csound". If this is not the location of theCsound program on your computer, reset the global variable *csound* to theappropriate command string that will work on your machine. If you do not haveCsound installed on your computer you can generate the score file but you willnot be able to run CSound. In this case you should not set an output hook.

Before generating our score we will also define a CSound header string toinclude in the top of the output score file. This string is not examined byCommon Music and so can contain any text that is legal in a Csound score file.

In our case the header string will contain a CSound f statement that defines anamplitude envelope for the linen unit generator used by i1. At this point we areready to output events to a Csound score file.

Interaction 24-2. Listening to blur.

cm> (set-sco-output-hook! #'play-sco-file)

cm> (define sco-header ; define a score file header string "f1 0 8192 10 0.0 0.41 0.06 0.04 0.00 0.11 0.02 0.00 0.00")

cm> (events (blur) "blur.sco" 0 :header sco-header :options "-d -m0 -A" :orchestra "blur.orc" :output "blur.aiff")

Page 310: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

"blur.sco"cm>

→ blur.sco→ blur.mp3

Example 24-4 contains the first few lines of the "blur.sco" score file created inInteraction 24-2:

Example 24-4. Contents of blur.sco

; Common Music 2.4.0 output on 24 Mar 2003, 09:49:38f1 0 8192 10 0.0 0.41 0.06 0.04 0.00 0.11 0.02 0.00 0.00i1 0 0.4 0.1 300 0.05 0.25i1 0.015 0.4 0.1 406 0.05 0.25i1 0.03 0.4 0.1 975 0.05 0.25...

Notice that the f statement from our header string appears before the firstinstrument call in the score file. After the score file was written our hook functionautomatically started the Csound program in "/usr/local/bin/csound" andpassed it the command options "-d -m0 -A", our score file "blur.sco" and theorchestra file "blur.orc". Csound then generated audio output from the score tothe file "blur.aiff".

Common Lisp Music

Common Lisp Music (CLM) is a sound synthesis language written by WilliamSchottstaedt at CCRMA, Stanford University. CLM is fast, efficient, has real-time capabilities and runs on many different types of computers. DespiteCLM's name, the software is actually written in Lisp and C. CLM uses C toimplement the low-level unit generators that perform the sample-by-samplecalculations to generate the audio signal. Lisp provides CLM with an interactivedevelopment environment for designing and working with instruments. SinceCLM and Common Music share the same Lisp environment, from the noviceLisp composers' perspective they may not appear to be separate softwaresystems at all!

Defining CLM instruments

Like Csound, CLM defines audio signals in terms of synthesis instrumentsacting on synthesis data. But in the case of CLM, both the audio instrumentsand the data they use are represented in Lisp. CLM instruments are justspecialized Lisp functions that generate audio signals. These instrumentfunctions are defined using CLM's definstrument macro. Definstrumentexpressions are normally placed in a Lisp source files with a “.ins” extensionand then compiled and loaded into Lisp. An “.ins” file can contain any number ofCLM instruments and is in every way analogous to a Csound orchestra file.

Page 311: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

For purposes of comparison, Example 24-5 shows the contents of aninstrument file called beep.ins that contains a single CLM instrument definition.The instrument is called beep and it is equivalent to the i1 Csound instrumentdefined earlier in this chapter.

Example 24-5. The contents of the CLM instrument file"beep.ins".

(definstrument beep (start dur freq amp amp-env) (let* ((beg (seconds->samples start)) (end (seconds->samples (+ start dur))) (osc (make-oscil :frequency freq)) (env (make-env :envelope amp-env :scaler amp :duration dur))) (run (loop for i from beg to end do (outa i (* (env env) (oscil osc)))))))

Like i1, the CLM beep instrument uses of an oscillator and an amplitudeenvelope. The let* establishes local variables used in the calculation of the

audio output signal. The variable beg is the first sample number to start

generating in the audio output file and end is the ending sample. The osc

variable holds an oscillator object and env is set to an envelope generator. Therun expression establishes the scope of the instrument's "run time" statements:expressions inside the run block execute to generate a stream of audiosamples. The runtime expression of beep consists of a loop that iterates over

each sample number i and sets the variable s to the next sample output from

the oscillator osc scaled by the output of the envelope generator env. The

sample s is then written to the open output audio file.

Compiling and loading instruments

Definstrument code must be complied and loaded into Lisp (Interaction 24-3).Compilation is a process that transforms program code (Lisp expressions) intoequivalent low-level machine instructions. Compiled machine code executesmany times faster than interpreted program code. Compilation is really anecessity for audio instruments due to the large amounts of data that theycompute. For example, a stereo, CD-quality audio signal requires thecalculation of 88,200 audio samples for each second of sound!

Rather than listen to the simple beep sound again we will take thisopportunity to compile and load two instruments from the CLM softwaredistribution. The main instrument is called fm-violin and it was developed byWilliam Schottstaedt when he was a graduate student at CCRMA. The sourcecode for the instrument is contained in the file v.ins located in the same

directory as this HTML chapter file. The instrument is an example of cascade

frequency modulation, in which a carrier frequency is modulated by a complexwave output from a cascade of three modulating oscillators. Despite itsinherent complexity, the fm-violin instrument is actually very easy to work with.See chapter 23 for a general discussion of frequency modulation as a

Page 312: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

[Function]

synthesis technique.

Interaction 24-3. Compiling and loading "v.ins".

cm> (cd "/Users/hkt/NM/24/")"/Users/hkt/NM/24/"cm> (compile-file "v.ins")"/Users/hkt/NM/24/v.pfasl"cm> (load "v")"/Users/hkt/NM/24/v.pfasl"cm> (compile-file "nrev.ins")"/Users/hkt/NM/24/nrev.pfasl"cm> (load "v")"/Users/hkt/NM/24/nrev.pfasl"cm>

LISP Note:The file extension for the compiled file will vary witheach Lisp implementation.

In Interaction 24-3 the function compile-file is used to compile the Lispprogram file containing the fm-violin instrument definition. The load function isthen used to load the complied code into Lisp. It is possible to load interpretedprogram files as well as compiled files using the load function. In either case,the effect of loading is to evaluate the contents of the loaded file. We alsocompiled and loaded a reverberation instrument called nrev that we will use toadd a “hall effect” to the audio output.

When the “v.ins” file is compiled and loaded both the fm-violin instrument aswell as a sound event object for the instrument is automatically defined. Thismeans that when you work with CLM instruments you do not have to usedefobject to define a new object class. This process happens automatically bydefinstrument. The fm-violin instrument has almost forty parameters, but onlythe first four parameters must be specified in order to produce a sound. Wecan use Common Music's object-parameters function to inspect the list ofparameters that the fm-violin contains.

(object-parameters object)

Returns a list of sound parameters associated with the specified object.

Interaction 24-4. Listing an object's parameters.

cm> (object-parameters (new midi))(time duration keynum amplitude channel)cm> (object-parameters (new fm-violin))(startime dur frequency amplitude fm-index amp-env periodic-vibrato-rate random-vibrato-rate periodic-vibrato-amplitude random-vibrato-amplitude noise-amount noise-freq ind-noise-freq ind-noise-amount amp-noise-freq amp-noise-amount gliss-env glissando-amount fm1-env fm2-env fm3-env fm1-rat fm2-rat fm3-rat fm1-index fm2-index fm3-index base frobber reverb-amount index-type

Page 313: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

degree distance degrees no-waveshaping denoise denoise-dur denoise-amp)cm>

The fm-morph process

One of the interesting properties of the fm-violin instrument is that it is capableof generating many different types of sound, not just violin timbres. The key toits flexibility lies, in part, with its many optional parameters. We will now

generate a sound file that demonstrates the effect of the fm index on the fm-violin's timbre. We start by defining a few envelopes that our instrument andprocess will use.

Example 24-6. Envelopes for the fm-violin example.

(define amp-env '(0 0 2 .5 3 .9 5 .6 75 .2 100 0))

(define morph-weight '(0 1 .2 10 .3 20 .35 50 .45 100 .5 100 .55 100 .65 25 .97 20 .98 10 1 1))

(define spike-env '(0 .01 .2 .02 .3 .05 .35 .09 .4 .1 .425 .15 .45 .2 .5 2.5 .55 .2 .575 .15 .6 .1 .65 .09 .7 .05 .8 .02 1. .01))

(define trough-env '(0 9 .2 8 .3 5 .35 4 .45 0 .5 0 .65 4 .96 8 1 9))

The amp-env variable holds the amplitude envelope that the fm-violin will use.The other three envelopes are used by the process to calculate parametervalues for fm-violin events. The morph-weight envelope is used to calculate therandom weight of each note that the process generates as a function of time.

The spike-env envelope controls the duration of each event, durations in themiddle portion of the score will last significantly longer than at either the

beginning or ending of the score. The trough-env envelope controls the fm-index of the process; its value is 9 at the outset and end of the process (quitelarge) and zero in the middle.

Example 24-7. The fm-morph process.

(define (fm-morph len rhy amp) (let* ((w1 1) (pat (new random :of ((c1 :weight .3) (c4 :weight,(pval w1)) cs4 d4 ds4 e4 f4 fs4 (g4 :weight ,(pval w1)) gs4 a4 as4 b4

Page 314: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

(c5 :weight ,(pval w1))) :for 1))) (process for i below len for pct = (/ i len) for frq = (next pat) for lo? = (scale= frq 'c1) do (set! w1 (interpl pct morph-weight)) output (new fm-violin :startime (now) :dur (if lo? 8 (interpl pct spike-env)) :amplitude amp :frequency (hertz frq) :amp-env amp-env :reverb-amount .1 :fm-index (if lo? 2.5 (interpl pct trough-env))) wait rhy)))

Generating an audio file

At this point we are ready to generate a CLM score. There are two possiblescore files that we can generate: a CLM score file or an audio file. A CLMscore file is a text file with a “.clm” extension that contains a time ordered seriesof instrument calls. A CLM audio file contains the audio signal itself. We willtake this opportunity to generate a stereo CD-quality audio file. Audio files canbe generated from CLM in “.aiff”, “.snd” or “.wav” formats simply by specifyingone of these as the file extension in the events function. A CLM score has manypossible initializations, only a few of the most basic are shown here:

:srate numberThe sampling rate of the audio file. The default is 22050.

:channels numberThe number of channels in the output audio file. The default is 1.

:trace booleanIf true then each event will print its instrument and start time as it is outputto the audio file.

:play booleanIf true then CLM will play the audio file after it is written. The default valueis true.

Consult the CLM documentation on with-sound for the complete list of audiofile initializations. At this point we are ready to output our audio file:

Interaction 24-5. Generating audio output from fm-morph.

cm> (events (fm-morph 400 .1 .2) "morph.aiff" 0 :srate 44100 :channels 2 :reverb 'nrev); File: "morph.aiff"; Channels: 2; Srate: 44100; Reverb: NREV

Page 315: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

"morph.aiff"cm>

→ morph.mp3

Common Music Notation

We conclude this chapter with a brief discussion of Common Music Notation(CMN), also written by William Schottstaedt at CCRMA, Stanford University.Common Music Notation is a program that transforms Lisp expressions into amusical manuscript that is saved in an Encapsulated Postscript file (EPS).CMN has no editor and is not designed as a substitute for notation programs.However, it does an excellent job of music manuscripting and it is particularlyuseful for producing visual output from algorithms. The algorithms can use midiobjects to represent the basic note data that CMN will display. While CMN'stime-line and layout algorithms are very sophisticated, best results can beachieved by following two simple guidelines. First, the midi event's keynumproperty should contain note names rather than key numbers. Second, tominimize problems with rhythmic quantization, time values should be specifiedusing exact ratios of a quarter note. This will be discussed in more detail in thenext section.

The CMN score

A CMN score is generated by specifying a file with an “.eps” or a “.cmn” fileextension to the events function. In either case a EPS file containing themanuscript is generated. If a “.cmn” file is specified then a CMN input file will besaved along with the EPS output. A CMN input file is a text file that contains theLisp expressions that generated the EPS manuscript. These expression canthen be edited in any text editor to produce a more refined or detailed score. ACMN score supports many initializations, consult the CMN documentation for acomplete listing. The most basic initialization are defined here:

:exact-rhythms booleanIf true then the CMN score parses time values as exact rhythms. Exact

CMN rhythms are ratios of the quarter note, not the whole note. Forexample, 1 means quarter note, 1/2 means eighth, 1/4 means 16th andso on. Using exact rhythms will avoid quantization errors that mightotherwise occur if CMN parses note events containing time valuesspecified in seconds.

:title stringThe title string to display in the output manuscript.

:size numberThe overall size of the score. The default value is 40; 24 produces amoderately small staff.

:staffing listA list of staff descriptions, see the next section for more information.

Page 316: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

CMN staff descriptions

A list of staff descriptions provides a way to determine the exact staff that eachevent will appear in when it is output by an algorithmic processes. The list ofstaff descriptions is specified to the :staffing initialization of a CMN scorewhen it is created by the events function. Each staff description is a list in theformat:

(id [:name s] [:clef sym] [:meter list])

The first element id in the staff description is an integer identifier for the staff.Events output by a process are matched against all the staff ids in the score todetermine which staff the object will appear in. In the case of MIDI events, thechannel number of the MIDI event is used to specify this staffing value. The:name attribute is an optional string name for the staff. If a :name is not provided

the name of the staff will be set to “staff-n” where n is the id of the staff. Thevalue of the :clef attribute is an optional symbol clef name or a list of clefnames. If this value is not specified then CMN will use whatever clef it thinksbest. If the clef value is :both then the grand staff is used. If the value is a list ofclefs it limits CMN's clef choices to the clefs mentioned in the list. The :meterattribute is an optional meter list for the staff, where the first element in the list isthe number of beats per measure and the second is the measure beat.Example 24-8 shows the definition of a staffing list for a score with three stavesof music:

Example 24-8. Example staff definitions.

(define trio-staves '((0 :name "Cello" :clef :bass :meter (4 4)) (1 :name "Oboe" :clef :treble :meter (4 4)) (2 :name "Flute" :clef :treble :meter (4 4))))

The staffing list in Example 24-8 species that any MIDI event with a channelvalue of 0 will appear in the staff named “Cello”, channel 1 events will be writtento the “Oboe” staff and channel 2 events will appear in the “Flute” staff.

Creating a CMN manuscript

We first define a little example process that outputs MIDI events to our CMNscore:

Example 24-9. defining a process to output CMN files.

(define duet-staves '((0 :name "Viola" :clef :alto :meter (4 4)) (2 :name "Flute" :clef :treble :meter (4 4))))

(define (duet-cmn stf len nts) (let ((nts (new heap :notes nts))

Page 317: Heinrich Taube - Notes From the Metalevel: An Introduction to Computer Composition

;; choose quarter or two eighths (rhy (new random :of (1 ,(new cycle :of '(1/2 1/2)))))) (process while (< (now) len) for n = (next nts) for r = (next rhy) output (new midi :time (now) :duration r :keynum n :channel stf) ; link to cmn staff wait r)))

Because our CMN example involves exact rhythms, the time values in the rhypattern are expressed using CMN rhythm ratios. (Since these values areproportional we could scale them by an arbitrary tempo factor if this processwere to be used for both MIDI file and CMN file output.)

Interaction 24-6. Outputting a CMN file.

cm> (events (list (duet-cmn 0 12 '(c3 d ef f g)) (duet-cmn 1 12 '(c5 d ef f g))) "duet.eps" :staffing duet-staves :size 24 :title "Hiho!")Manuscripting duet.eps..."duet.eps"cm> (events (list (ms-cmn 0 12 '(c3 d ef f g)) (ms-cmn 1 12 '(c5 d ef f g))) "duet.cmn" :staffing duet-staves :size 24 :title "Hiho!")"duet.cmn"cm>

The file duet.pdf is a PDF version of duet.eps and the file duet.cmn contains theCMN input expressions that created it.

Chapter Source Code

The source code to all of the examples and interactions in this chapter can befound in the file beyond.cm located in the same directory as the HTML file forthis chapter. The source file can be edited in a text editor or evaluated insidethe Common Music application.

References

Dodge, C. & Jerse, T. (1985). Computer Music. New York: Schirmer Books.