You are on page 1of 773

The SuperCollider Book

edited

by

Scott

Wilson, David Cottle, and Nick Collins

The MIT Press

Cambridge, Massachusetts
London, England

Contents

Foreword ix
James McCartney
Introduction xiii
Scott Wilson, David Cottle, and Nick Collins
Tutorials

1
1
2
3
4

Advanced Tutorials
5
6
7
8

Beginners Tutorial 3
David Cottle
The Unit Generator 55
Joshua Parmenter
Composition with SuperCollider 81
Scott Wilson and Julio dEscrivn
Ins and Outs: SuperCollider and External Devices 105
Stefan Kersten, Marije A. J. Baalman, and Till Bovermann
125
Programming in SuperCollider 127
Iannis Zannos
Events and Patterns 179
Ron Kuivila
Just-in-Time Programming 207
Julian Rohrhuber and Alberto de Campo
Object Modeling 237
Alberto de Campo, Julian Rohrhuber, and Till Bovermann

vi

Contents

Platforms and GUI

271

9
10
11
12

Practical Applications
13

14
15
16
17
18

Mac OSX GUI 273


Jan Trtzschler von Falkenstein
SwingOSC 305
Hanns Holger Rutz
SuperCollider on Windows 339
Christopher Frauenberger
Collision with the Penguin: SuperCollider on Linux
Stefan Kersten and Marije A. J. Baalman
379

Sonication and Auditory Display in SuperCollider 381


Alberto de Campo, Julian Rohrhuber, Till Bovermann, and Christopher
Frauenberger
Spatialization with SuperCollider 409
Marije A. J. Baalman and Scott Wilson
Machine Listening in SuperCollider 439
Nick Collins
Microsound 463
Alberto de Campo
Alternative Tunings with SuperCollider 505
Fabrice Mogini
Non-Real-Time Synthesis and Object-Oriented Composition 537
Brian Willkie and Joshua Parmenter

Projects and Perspectives


19
20

21
22
23

355

573

A Binaural Simulation of Varses Pome lectronique 575


Stefan Kersten, Vincenzo Lombardo, Fabrizio Nunnari, and Andrea Valle
High-Level Structures for Live Performance: dewdrop_lib and
chucklib 589
James Harkins
Interface Investigations 613
Thor Magnusson
SuperCollider in Japan 629
Takeko Akamatsu
Dialects, Constraints, and Systems within Systems 635
Julian Rohrhuber, Tom Hall, and Alberto de Campo

vii

Contents

Developer Topics
24
25
26

657
The SuperCollider Language Implementation
Stefan Kersten
Writing Unit Generator Plug-ins 691
Dan Stowell
Inside scsynth 721
Ross Bencina

Appendix: Syntax of the SuperCollider Language


Iannis Zannos
Subject Index 745
Code Index 751

659

741

Foreword
James McCartney

Why use a computer programming language for composing music? Specically, why
use SuperCollider? There are several very high-level language environments for
audio besides SuperCollider, such as Common Music, Kyma, Nyquist, and Patchwork. These all demonstrate very interesting work in this area and are worth looking into. SuperCollider, though, has the unique combination of being free, well
supported, and designed for real time. It is a language for describing sound processes. SuperCollider is very good at allowing one to create nice sounds with minimal
effort, but more important, it allows one to represent musical concepts as objects, to
transform them via functions or methods, to compose transformations into higherlevel building blocks, and to design interactions for manipulating music in real time,
from the top-level structure of a piece down to the level of the waveform. You can
build a library of classes and functions that become building blocks for your working
style and in this way make a customized working environment. With SuperCollider,
one can create many things: very long or innitely long pieces, innite variations of
structure or surface detail, algorithmic mass production of synthesis voices, sonication of empirical data or mathematical formulas, to name a few. It has also been
used as a vehicle for live coding and networked performances. Because of this openendedness, early on, I often felt it difcult to know how best to write the documentation. There were too many possible approaches and applications.
Thus, I am pleased that there will now be a book on SuperCollider, and the best
part of it for me is that I have not had to do much of the hard work to get it done.
Since I made SuperCollider open source, it has taken on a life of its own and become
a community-sustained project as opposed to being a project sustained by a single
author. Many people have stepped up and volunteered to undertake tasks of documentation, porting to other operating systems, interfacing to hardware and software, writing new unit generators, extending the class library, maintaining a Web
site, mailing lists, and a wiki, xing bugs, and, nally, writing and editing the chapters of this book. All of these efforts have resulted in more features, better documentation, and a more complete, robust, and bugfree program.

James McCartney

SuperCollider came about as the latest in a series of software synthesis languages


that I have written over the years. I have been interested in writing software to synthesize electronic music ever since I was in high school. At that time, I had written
on a piece of notebook paper a set of imaginary subroutine calls in BASIC for implementing all of the common analog synthesizer modules. Of course, doing audio
synthesis in BASIC on the hardware of that time was completely impractical, but the
idea had become a goal of mine. When I graduated college, I went to have a look at
E-mu in California and found that it was operating out of a two-story house. I gured that the synthesizer industry was a lot smaller than I had imagined and that I
should rethink my career plans.
The rst software synthesizer I wrote was a graphical patching environment called
Synfonix that generated samples for the Ensoniq Mirage sampling keyboard using a
Macintosh computer. I attempted to sell this program but sold only two copies, one
to Ivan Tcherepnin at Harvard and another to Mark Polishook. A business lesson I
learned from this is not to sell a product that requires purchasers to already own two
niche products. The intersection of two small markets is near zero.
In 1990, I wrote a program called Synth-O-Matic that I used personally but never
meant to distribute, even though one copy I gave to a friend got circulated. I created
this program after learning CSound and deciding that I never wanted to actually
have to use CSounds assembly-language-like syntax. Synth-O-Matic had a more
expression-oriented syntax for writing signal ow graphs and a graphical user interface for editing wave tables. I used this program on and off, but it was quite slow so
I stopped using it, for the most part, in favor of hardware synthesizers.
It wasnt until the PowerPC came out that it became practical to do oating-point
signal processing in real time on a personal computer. At the time I had been working on music for a modern dance piece, using Synth-O-Matic to do granular synthesis.
It was taking a long time to generate the sound, and I was running behind schedule
to get the piece done. On the day in March 1994 when the rst PowerPC-based machine came out, I went and bought the fastest one. I recompiled my code, and it ran
32 times faster. I was then able to complete the piece on time. I noticed that my code
was now running faster than real time, so I began working on a program designed
to do real-time synthesis. Around this time I got a note in the mail from Curtis
Roads, who had apparently gotten one of the circulating copies of Synth-O-Matic,
encouraging me to further develop the program. So I took the Synth-O-Matic engine
and combined it with the Pyrite scripting language object which I had written for
MAX. This became SuperCollider version 1, which was released in March 1996.
The rst two orders were from John Bischoff and Chris Brown of The Hub.
The name SuperCollider has an amusing origin. During the early 1990s I
worked in the Astronomy Department of the University of Texas at Austin on the
Hubble Space Telescope Astrometry Science Team, writing software for data analysis

xi

Foreword

and telescope observation planning. On the oors below my ofce was the Physics
Department, some of the members of which were involved in the Superconducting
Super Collider project. In 1993, Congress cut the funding for the project, and there
were many glum faces around the building after that. I had been thinking about this
merging, or collision, if you will, of a real-time synthesis engine with a high-level
garbage collected language, and it seemed to me that it was an experiment that
would likely fail, so I named it after the failed Big Science project of the day. Except
that the experiment didnt fail. To my surprise, it actually worked rather well.
The version 1 language was dynamically typed, with a C-like syntax, closures borrowed from Scheme, and a limited amount of polymorphism. After using version 1
for a couple of years, and especially after a project on which I was invited by Iannis
Zannos to work on a concert in an indoor swimming pool in Berlin, I realized that
it had severe limitations on its ability to scale up to create large working environments. So I began working on version 2, which borrowed heavily from Smalltalk. It
was for the most part the same as the language described in this book except for the
synthesis engine and class library, which have changed a great deal.
The goal of SuperCollider version 2 and beyond was to create a language for describing real-time interactive sound processes. I wanted to create a way to describe categories of sound processes that could be parameterized or customized. The
main idea of SuperCollider is to algorithmically compose objects to create soundgenerating processes. Unit generators, a concept invented by Max Matthews for his
Music N languages, are very much like objects and are a natural t for a purely
object-oriented Smalltalk-like language.
In 2001 I was working on version 3 of SuperCollider, and because of the architecture of the server, it was looking like it really should be open source, so that anyone
could modify it however they liked. I was (barely) making a living at the time selling
SuperCollider, so the decision to give it away was a difcult one to make. But nancially it was looking like I would need a real job soon, anyway. I was also worried
that the period of self-employment on my rsum would begin looking suspect to
potential employers. Ultimately, I did get a job, so I was able to open source the
program. On the day that I made all of my previously copyright-protected programs
free, my Web site experienced an eightfold increase in trafc. So obviously there was
a lot of interest in an open source audio language and engine, especially one that is
free.
I hope that this book will enable and inspire the reader to apply this tool in useful
and interesting ways. And I hope to hear and enjoy the results!

Introduction
Scott Wilson, David Cottle, and Nick Collins

Welcome to The SuperCollider Book. Were delighted to present a collection of tutorials, essays, and projects that highlight one of the most exciting and powerful
audio environments. SuperCollider (SC to its friends) is a domain-specic programming language specialized for sound but with capabilities to rival any generalpurpose language. Though it is technically a blend of Smalltalk, C, and ideas from a
number of other programming languages, many users simply accept SuperCollider
as its own wonderful dialect, a superlative tool for real-time audio adventures. Indeed, for many artists, SuperCollider is the rst programming language they learn,
and they do so without fear because the results are immediately engaging; you can
learn a little at a time in such a way that you hardly notice that youre programming
until its too late and youre hooked! The potential applications in real-time interaction, installations, electroacoustic pieces, generative music, audiovisuals, and a host
of other possibilities make up for any other qualms. On top of that, its free, powerful, and open source, and has one of the most supportive and diverse user and developer communities around.
Pathways
This book will be your companion to SuperCollider; some of you will already have
experience and be itching to turn to the many and varied chapters further on from
this point. Well let you follow the book in any order you choose! But we would like
to take care to welcome any newcomers and point them straight in the direction of
chapter 1, which provides a friendly introduction to the basics. For those on Windows or Linux it may be read together with chapter 11 or 12, respectively, which
cover some of the cross-platform installation issues. From there we suggest beginners continue through until chapter 4, as this path will provide you with some basic
skills and knowledge which can serve as a foundation for further learning.
For more advanced users, we suggest you look at the more topics-oriented
chapters which follow. These chapters arent designed to be read in any particular

xiv

Scott Wilson, David Cottle, and Nick Collins

order, so proceed with those of particular interest and relevance to you and your
pursuits. Naturally we have referred to other chapters for clarication where necessary and have tried to avoid duplication of materials except where absolutely crucial
for clarity.
These topics chapters are divided into sections titled Advanced Tutorials, Platforms and GUI, and Practical Applications. They begin with chapter 5, Programming in SuperCollider, which provides a detailed overview of SuperCollider as a
programming language. This may be of interest to beginners with a computer science
background whod rather approach SC from a language design and theory perspective than through the more user-friendly approach in chapter 1. Chapters on a variety
of subjects follow, including sonication, spatialization, microsound, GUIs, machine
listening, alternative tunings, and non-real-time synthesis.
Following these chapters is a section for intermediate and advanced users titled
Projects and Perspectives. The material therein provides examples of how SuperCollider has been used in the real world. These chapters also provide some philosophical insight into issues of language design and its implications (most specically
in chapter 23, Dialects, Constraints, and Systems-Within-Systems). This sort of
intellectual pursuit has been an important part of SuperColliders development; SC
is a language that self-consciously aims for good design, and to allow and encourage
elegance, and even beauty, in the users code. Although this might seem a little abstract at rst, we feel that this sets SC apart from other computer music environments and that as users advance, awareness of such things can improve their code.
Finally, there is a section titled Developer Topics, which provides detailed under
the hood information on SC. These chapters are for advanced users seeking a
deeper understanding of the SC and its workings and for those wishing to extend SC,
for instance, by writing custom unit generator plug-ins.
Code Examples and Text Conventions
Initially SuperCollider was Mac only, but as an open source project since 2001, it
has widened its scope to cover all major platforms, with only minor differences between them. Most code in this book should run on all platforms with the same results, and we will note places where there are different mechanisms in place; most of
the time, the code itself will already have taken account of any differences automatically. For instance, SC includes cross-platform GUI classes such as View, Slider,
and Window. These will automatically redirect to the correct GUI implementation,
either Cocoa (on Mac OSX; see chapter 9) or SwingOSC (on all platforms; see chapter 10). However, there are some differences in the programming editor environments (such as available menu items) and the keyboard shortcuts. You are referred

xv

Introduction

as well to the extensive Help system that comes with the SuperCollider application;
a Help le on keyboard Shortcuts for the various platforms is prominently linked
from the main Help page.
Just to note, when you come across keyboard shortcuts in the text, theyll appear
like this: [enter] designates the enter (and not the return) key, [ctrl+a] means the
control key plus the a key, and so on. Furthermore, all text appearing in the FRGH
IRQW will almost always be valid SuperCollider code (very occasionally there may be
exceptions for didactic purposes, such as here). You will also encounter some special
SuperCollider terms (e.g., Synth, SynthDef, and Array) that arent in code font and
are discussed in a friendly manner; this is because they are ubiquitous concepts and
it would be exhausting to have them in the code font every time. You may also see
them appearing with a capital letter (i.e., Synths), or all lower case (synths), depending again on how formal we are being. Anyway, if youre new to SuperCollider, dont
worry about this at all; chapter 1 will start you on the righteous path, and youll
soon be chatting about Synths and UGens like the rest of us.
The Book Web Site
This brings us to the accompanying Web site for the book (<http://supercolliderbook
.net>), which contains all the code reproduced within, ready to run, as well as download links to the application itself, its source code, and all sorts of third-party extras,
extensions, libraries, and examples. A standardized version of SuperCollider is used
for the book, SuperCollider 3.4, for which all book code should work without trouble. Of course, the reader may nd it productive to download newer versions of
SuperCollider as they become available, and it is our intention to provide updated
versions of the example code where needed. Although we can make no hard promises, in this fast-paced world of operating system shifts, that the code in this book
will remain eternally correctthe ongoing development and improvement of environments such as SuperCollider are a big part of what makes them so exciting
weve done our best to present you with a snapshot of SuperCollider that should
retain a core validity in future years.
Final Thoughts
Please be careful with audio examples; there is of course the potential to make noises
that can damage your hearing if youre not sensible with volume levels. Until you
become accustomed to the program, we suggest you start each example with the
volume all the way down, and then slowly raise it to a comfortable level. (And if
youre not getting any sound, remember to check if youve left the monitors off or

xvi

Scott Wilson, David Cottle, and Nick Collins

the computer audio muted.) Some examples may use audio input and have the potential to feedback unless your monitoring arrangements are correct. The easiest way
to deal with such examples is to monitor via headphones.
We couldnt possibly cover everything concerning SuperCollider, and there are
many online resources to track down new developments and alternative viewpoints,
including mailing lists, forums, and a host of artists, composers, technology developers, and SuperCollider maniacs with interesting pages. We have provided a few of
the most important links (at the time of writing) below, but Wikigoopedigle, or
whatever your contemporary equivalent is, will allow you to search out the current
SuperCollider 15.7 as necessary.
Were sure youll have fun as you explore this compendium, and were also sure
youll be inspired to some fantastic art and science as you go. Enjoy exploring the
SuperCollider world rst charted by James McCartney but since expanded immeasurably by everyone who partakes in this innitely exible open source project.
Primary Web Resources
Main community home page: <http://supercollider.sourceforge.net>
Application download and project site: <http://sourceforge.net/projects/
supercollider>
James McCartneys home page: <http://www.audiosynth.com>
The venerable swiki site: <http://swiki.hfbk-hamburg.de:8888/MusicTechnology/6>
Acknowledgments
We owe a debt of gratitude to the chapter contributors and to the wider SuperCollider
community, who have supported this project. SCs community is one of its greatest
strengths, and innumerable phone calls, e-mails, chats over cups of tea at the SC
Symposium, and many other interactions, have contributed to making this book and
SC itself stronger. We apologize to all who have put up with our insistent editing and
acknowledge the great efforts of the developers to prepare a stable SuperCollider 3.4
version for this book. A thousand thank-yous:
GR^GRPRDULJDWRJR]DLPDVKLWDSRVWOQ`

Thanks to all at MIT Press who have assisted in the handling of our proposal and
manuscript for this book.
Thank you to friends, family, and colleagues who had to deal with us while we
were immersed in the lengthy task of organizing, editing, and assembling this book.
Editing may seem like a solitary business, but from their perspective were quite sure
it was a team effort!

xvii

Introduction

Many thanks to our students, who have served as guinea pigs for pedagogical approaches, tutorial materials, experimental developments, and harebrained ideas.
Now back to your exercise wheels!
Finally, the editors would like to thank each other for support during the period
of gestation. At the time of writing, weve been working on this project for 2 years
to bring the nal manuscript to fruition. Though Scott and Nick have met on many
occasions (and were co-organizers of the 2006 SuperCollider Symposium at the University of Birmingham), neither has ever met David in person (we sometimes wonder
if he really exists!); but you should see the number of e-mails weve sent each other.
For many months, in the heat of the project, David modied his routine to include a
3 A.M. e-mail check to keep up with those using GMT, which imparts a celestially
imposed 8-hour advantage. In any case, although it has at times been exhausting,
seeing this book through to fruition has been a pleasure, and we hope it brings you
pleasure to read it and learn about SC.

Composition with SuperCollider


Scott Wilson and Julio dEscrivn

3.1

Introduction
The actual process of composing, and deciding how to go about it, can be one of
the most difcult things about using SuperCollider. People often nd it hard to
make the jump from modifying simple examples to producing a full-scale piece. In
contrast to Digital Audio Workstation (DAW) software such as Pro Tools, for example, SC doesnt present the user with a single preferred way of working. This
can be confusing, but its an inevitable side effect of the exibility of SC, which allows for many different approaches to generating and assembling material. A brief
and incomplete list of ways people might use SC for composition could include the
following:
Real-time interactive works with musicians
Sound installations
Generating material for tape music composition (to be assembled later on a DAW),
perhaps performed in real time
As a processing and synthesis tool kit for experimenting with sound
To get away from always using the same plug-ins
To create generative music programs
To create a composition or performance tool kit tailored to ones own musical
ideas.

All of these activities have different requirements and suggest different approaches.
This chapter attempts to give the composer or sound artist some starting points for
creative exploration. Naturally, we cant hope to be anywhere near exhaustive, as
the topic of the chapter is huge and in some senses encompasses all aspects of SC.
Thus well take a pragmatic approach, exploring both some abstract ideas and concrete applications, and referring you to other chapters in this book where they are
relevant.

82

Scott Wilson and Julio dEscrivn

3.1.1 Coding for Flexibility


The notion of making things that are exible and reusable is something that well
keep in mind as we examine different ideas in this chapter. As an example, you might
have some code that generates a nished sound le, possibly your entire piece. With
a little planning and foresight, you might be able to change that code so that it can
easily be customized on the y in live performance, or be adapted to generate a new
version to different specications (quad instead of stereo, for instance).
With this in mind, it may be useful to utilize environment variables which allow
for global storage and are easily recalled. Youll recall from chapter 1 that environment variables are preceded by a tilde (~).
VRPHFRGHZHPD\ZDQWWRXVHODWHU
aVRPHWKLQJ ^3XOVHDU  (QY*HQDU (QYSHUFGRQH$FWLRQ `
ZKHQWKHWLPHFRPHVMXVWFDOOLWE\LWVQDPHDQGSOD\LW
aVRPHWKLQJSOD\

Since environment variables do not have the limited scope of normal variables,
well use them in this chapter for creating simple examples. Keep in mind, however,
that in the nal version of a piece there may be good reasons for structuring your
code differently.
3.2

Control and Structure


When deciding how to control and structure a piece, you need to consider both practical and aesthetic issues: Who is your piece for? Who is performing it? (Maybe you,
maybe an SC Luddite . . .) What kind of exibility (or expressiveness!) is musically
meaningful in your context? Does pragmatism (i.e., maximum reliability) override
aesthetic or other concerns (i.e., youre a hard-core experimentalist, or you are on
tenure track and need to do something technically impressive)?
A fundamental part of designing a piece in SC is deciding how to control what
happens when. How you do this depends upon your individual needs. You may
have a simple list of events that need to happen at specic times, or a collection of
things that can be triggered exibly (for instance, from a GUI) in response to input
from a performer, or algorithmically. Or you may need to combine multiple
approaches.
We use the term structure here when discussing this issue of how to control when
and how things happen, but keep in mind that this could mean anything from the
macro scale to the micro scale. In many cases in SC the mechanisms you use might
be the same.

83

Composition with SuperCollider

3.2.1 Clocks, Routines, and Tasks


Heres a very simple example that shows you how to schedule something to happen
at a given time. It makes use of the 6\VWHP&ORFN class.
6\VWHP&ORFNVFKHG ^IRRSRVWOQ` 

The rst argument to the VFKHG message is a delay in seconds, and the second is a
that will be evaluated after that delay. In this case the Function simply
posts the word foo, but it could contain any valid SC code. If the last thing to be
evaluated in the Function returns a number, SystemClock will reschedule the Function, using that value as the new delay time.

)XQFWLRQ

IRRUHSHDWVHYHU\VHFRQG
6\VWHP&ORFNVFKHG ^IRRSRVWOQ` 
EDUUHSHDWVDWDUDQGRPGHOD\
6\VWHP&ORFNVFKHG ^EDUSRVWOQUDQG` 
FOHDUDOOVFKHGXOHGHYHQWV
6\VWHP&ORFNFOHDU

SystemClock has one important limitation: it cannot be used to schedule events


which affect native GUI widgets on OSX. For this purpose another clock exists,
called $SS&ORFN. Generally you can use it in the same way as SystemClock, but be
aware that its timing is slightly less accurate. There is a shortcut for scheduling
something on the AppClock immediately, which is to wrap it in a Function and call
GHIHU on it.
FDXVHVDQRSHUDWLRQFDQQRWEHFDOOHGIURPWKLV3URFHVVHUURU
6\VWHP&ORFNVFKHG ^6&:LQGRZQHZIURQW` 
GHIHUUHVFKHGXOHV*8,FRGHRQWKH$SS&ORFNVRWKLVZRUNV
6\VWHP&ORFNVFKHG ^^6&:LQGRZQHZIURQW`GHIHU` 

GUI, by the way, is short for Graphical User Interface and refers to things such as
windows, buttons, and sliders. This topic is covered in detail in chapters 9 and 10,
so although well see some GUI code in a few of the examples in this chapter, we
wont worry too much about the nitty-gritty details of it. Most of it should be pretty
straightforward and intuitive, anyway, so for now, just move past any bits that arent
clear and try to focus on the topics at hand.
Another Clock subclass, 7HPSR&ORFN, provides the ability to schedule events according to beats rather than in seconds. Unlike the clocks weve looked at so far, you
need to create an instance of TempoClock and send sched messages to it, rather than
to the class. This is because you can have many instances of TempoClock, each with
its own tempo, but theres only one each of SystemClock and AppClock. By varying

84

Scott Wilson and Julio dEscrivn

a TempoClocks tempo (in beats per second), you can change the speed. Heres a
simple example.

W 7HPSR&ORFNQHZPDNHDQHZ7HPSR&ORFN
WVFKHG ^+HOORSRVWOQ` 

WWHPSR WZLFHDVIDVW
WFOHDU

TempoClock also allows beat-based and bar-based scheduling, so it can be particularly useful when composing metric music. (See the TempoClock Help le for
more details.)
Now lets take a look at Routines. A 5RXWLQH is like a Function that you can
evaluate a bit at a time, and in fact you can use one almost anywhere youd use a
Function. Within a Routine, you use the yield method to return a value and pause
execution. The next time you evaluate the Routine, it picks up where it left off.

U 5RXWLQH ^
IRR\LHOG
EDU\LHOG
` 

UYDOXHIRR
UYDOXHEDU
UYDOXHZH
YHUHDFKHGWKHHQGVRLWUHWXUQVQLO

Routine has a commonly used synonym for YDOXH, which is QH[W. Although next
might make more sense semantically with a Routine, value is sometimes preferable, for reasons well explore below.
Now heres the really interesting thing: since a Routine can take the place of a
Function, if you evaluate a Routine in a Clock, and yield a number, the Routine will
be rescheduled, just as in the SystemClock example above.

U 5RXWLQH ^
IRRSRVWOQ
\LHOGUHVFKHGXOHDIWHUVHFRQG
EDUSRVWOQ
\LHOG
IRREDUSRVWOQ
` 
6\VWHP&ORFNVFKHG U 

85

Composition with SuperCollider

)HUPDWD
VERRW

U 5RXWLQH ^

[ 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 

ZDLW


[UHOHDVH  

\ 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 

:DLWLQJSRVWOQ

QLO\LHOGIHUPDWD


\UHOHDVH  

] 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 

ZDLW

]UHOHDVH
` 

GRWKLVWKHQZDLWIRUWKHIHUPDWD
USOD\
IHHOWKHVZHHWWRQLF
USOD\

Figure 3.1
A simple Routine illustrating a musical use of yield.

Figure 3.1 is a (slightly) more musical example that demonstrates a fermata of


arbitrary length. This makes use of ZDLW, a synonym for \LHOG, and of Routines SOD\
method, which is a shortcut for scheduling it in a clock. By yielding nil at a certain
point, the clock doesnt reschedule, so youll need to call play again when you want
to continue, thus releasing the fermata. Functions understand a message called
IRUN, which is a commonly used shortcut for creating a Routine and playing it in a
Clock.

^
VRPHWKLQJSRVWOQ
ZDLW
VRPHWKLQJHOVHSRVWOQ
`IRUN

Figure 3.2 is a similar example with a simple GUI control. This time well use a
7DVN, which you may remember from chapter 1. A Task works almost the same way

86

Scott Wilson and Julio dEscrivn


W 7DVN ^

ORRS ^ ORRSWKHZKROHWKLQJ


GR ^ GRWKLVWLPHV



[UHOHDVH  



[ 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 



ZDLW



[UHOHDVH  



[ 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 



ZDLW


` 


,
PZDLWLQJIRU\RXWRSUHVVUHVXPHSRVWOQ


QLO\LHOGIHUPDWD


[UHOHDVH  


[ 6\QWK ?GHIDXOW>IUHTPLGLFSV@ 


ZDLW


[UHOHDVH

` 
` 
Z :LQGRZQHZ 7DVN([DPSOH5HFW  IURQW
ZYLHZGHFRUDWRU )ORZ/D\RXW ZYLHZERXQGV 
%XWWRQQHZ Z5HFW  VWDWHVB >>3OD\5HVXPH&RORUEODFN
&RORUFOHDU@@

DFWLRQB ^WUHVXPH  ` 
%XWWRQQHZ Z5HFW  VWDWHVB >>3DXVH&RORUEODFN&RORUFOHDU@@

DFWLRQB ^WSDXVH` 
%XWWRQQHZ Z5HFW  VWDWHVB >>)LQLVK&RORUEODFN&RORUFOHDU@@

DFWLRQB ^


WVWRS


[UHOHDVH  


ZFORVH

` 

Figure 3.2
Using Task so you can pause the sequence.

87

Composition with SuperCollider

that a Routine does, but is meant to be played only with a Clock. A Task provides
some handy advantages, such as the ability to pause. As well, it prevents you from
accidentally calling play twice. Try playing with the various buttons and see what
happens.
Note that the example above demonstrates both xed scheduling and waiting for
a trigger to continue. The trigger neednt be from a GUI button; it can be almost
anything, for instance, audio input. (See chapter 15.)
By combining all of these resources, you can control events in time in pretty complicated ways. You can nest Tasks and Routines or combine xed scheduling with
triggers; in short, anything you like. Figure 3.3 is an example that adds varying
tempo to the mix, as well as adding some random events.
You can reset a Task or Routine by sending it the UHVHW message.
UUHVHW

3.2.2 Other Ways of Controlling Time in SC


There are 2 other notable methods of controlling sequences of events in SC: Patterns
and the Score object. Patterns provide a high-level abstraction based on Streams of
events and values. Since Patterns and Streams are discussed in chapter 6, we will not
explore their workings in great detail at this point, but it is worth saying that Patterns often provide a convenient way to produce a Stream of values (or other objects), and that they can be usefully combined with the methods shown above.
Figure 3.4 demonstrates two simple Patterns: 3VHT and 3[UDQG. Pseq species an
ordered sequence of objects (here numbers used as durations of time between successive events) and a number of repetitions (in this case an innite number, indicated by
the special value LQI). Pxrand also has a list (used here as a collection of pitches), but
instead of proceeding through it in order, a random element is selected each time.
The x indicates that no individual value will be selected twice in a row.
Patterns are like templates for producing Streams of values. In order to use a Pattern, it must be converted into a 6WUHDP, in this case using the DV6WUHDP message.
Once you have a Stream, you can get values from it by using the QH[W or YDOXH messages, just as with a Routine. (In fact, as you may have guessed, a Routine is a type
of Stream as well.) Patterns are powerful because they are reusable, and many
Streams can be created from 1 Pattern template. (Chapter 6 will go into more detail
regarding this.)
As an aside, and returning to the idea of exibility, the YDOXH message above demonstrates an opportunity for polymorphism, which is a fancy way of saying that
different objects understand the same message.1 Since all objects understand value
(most simply return themselves), you can substitute any object (a )XQFWLRQ, a

88

Scott Wilson and Julio dEscrivn


U 5RXWLQH ^

F 7HPSR&ORFNQHZPDNHD7HPSR&ORFN

VWDUWD
ZREEO\
ORRS

W 7DVN ^


ORRS ^



[UHOHDVH  



[ 6\QWK ?GHIDXOW>IUHTPLGLFSVDPS@ 



ZDLW



[UHOHDVH  



[ 6\QWK ?GHIDXOW>IUHTPLGLFSVDPS@ 



UUDQG  ZDLWUDQGRPZDLWIURPWRVHFRQGV


` 

`F XVHWKH7HPSR&ORFNWRSOD\WKLV7DVN

WVWDUW

QLO\LHOG


QRZDGGVRPHQRWHV

\ 6\QWK ?GHIDXOW>IUHTPLGLFSVDPS@ 

QLO\LHOG

\UHOHDVH  

\ 6\QWK ?GHIDXOW>IUHTPLGLFSVDPS@ 

FWHPSR GRXEOHWLPH

QLO\LHOG

WVWRS\UHOHDVH  [UHOHDVH  VWRSWKH7DVNDQG6\QWKV
` 

UQH[WVWDUWORRS
UQH[WILUVWQRWH
UQH[WVHFRQGQRWHORRSJRHV
GRXEOHWLPH

UQH[WVWRSORRSDQGIDGH

Figure 3.3
Nesting Tasks inside Routines.

89

Composition with SuperCollider

UDQGRPQRWHVIURPO\GLDQEVFDOH
S 3[UDQG >@LQI DV6WUHDP
RUGHUHGVHTXHQFHRIGXUDWLRQV
T 3VHT >@LQI DV6WUHDP
W 7DVN ^

ORRS ^


[UHOHDVH  


[ 6\QWK ?GHIDXOW>IUHTSYDOXHPLGLFSV@ 


TYDOXHZDLW

` 
` 
WVWDUW

WVWRS[UHOHDVH  

Figure 3.4
Using Patterns within a Task.
5RXWLQH,

a number, etc.) that will return an appropriate value for S or T in the example above. Since S and T are evaluated each time through the loop, its even possible to do this while the 7DVN is playing. (See gure 3.5.) Taking advantage of
polymorphism in ways like this can provide great exibility, and can be useful for
anything from generic compositions to algorithmically variable compositions.
The second method of controlling event sequences is the 6FRUH object. Score is essentially an ordered list of times and 26& commands. This takes the form of nested
Arrays. That is,
>
>WLPH>FPG@@
>WLPH>FPG@@

@

As youll recall from chapter 2, OSC stands for Open Sound Control, which is the
network protocol SC uses for communicating between language and server. What
you probably didnt realize is that it is possible to work with OSC messages directly,
rather than through objects such as Synths. This is a rather large topic, so since the
OSC messages which the server understands are outlined in the Server Command
Reference Help le, well just refer you there if youd like to explore further. In any
case, if you nd over time that you prefer to work in messaging style rather than
object style, you may nd 6FRUH useful. Figure 3.6 provides a short example. Score
also provides some handy functionality for non-real-time synthesis (see chapter 18).

90

Scott Wilson and Julio dEscrivn


S DFRQVWDQWQRWH
T 3VHT >@LQI DV6WUHDPRUGHUHGVHTXHQFHRIGXUDWLRQV
W 7DVN ^

ORRS ^


[UHOHDVH  


[ 6\QWK ?GHIDXOW>IUHTSYDOXHPLGLFSV@ 


TYDOXHZDLW

` 
` 
WVWDUW

QRZFKDQJHS
S 3VHT >@LQI DV6WUHDPWRD3DWWHUQGRUHPL
S ^UUDQG  `WRD)XQFWLRQUDQGRPQRWHVIURPD
FKURPDWLFRFWDYH
WVWRS[UHOHDVH  

Figure 3.5
Thanks to polymorphism, we can substitute objects that understand the same message.


6\QWK'HI 6FRUH6LQH^DUJIUHT 
2XWDU 

6LQ2VFDU IUHT  /LQHNU GRQH$FWLRQ

` DGG
[ >
DUJVIRUVBQHZDUHV\QWKGHIQRGH,'DGG$FWLRQWDUJHW,'V\QWKDUJV
>>?VBQHZ?6FRUH6LQH?IUHT@@
>>?VBQHZ?6FRUH6LQH?IUHT@@
>>?VBQHZ?6FRUH6LQH?IUHT@@
>>?FBVHW@@GXPP\FRPPDQGWRPDUNHQGRI157V\QWKHVLVWLPH
@
] 6FRUH [ 

]SOD\

Figure 3.6
Using messaging style: Score.

91

Composition with SuperCollider


KHUH
VDV\QWKGHIWKDWDOORZVXVWRSOD\IURPDEXIIHUZLWKDIDGHRXW
6\QWK'HI SOD\EXI^DUJRXW EXIJDWH 

2XWDU RXW


3OD\%XIDU EXI%XI5DWH6FDOHNU EXI ORRS 



/LQHQNU JDWHGRQH$FWLRQ UHOHDVHV\QWKZKHQIDGHGRQH


` DGG
ORDGDOOWKHSDWKVLQWKHVRXQGVIROGHULQWREXIIHUV
aVRPH6RXQGV VRXQGV SDWK0DWFKFROOHFW^_SDWK_%XIIHUUHDG VSDWK `

QRZKHUH
VWKHVFRUHVRWRVSHDN
H[HFXWHWKHVHRQHOLQHDWDWLPH
aQRZ3OD\LQJ 6\QWK SOD\EXI>EXIaVRPH6RXQGV>@@ 
aQRZ3OD\LQJUHOHDVHaQRZ3OD\LQJ 6\QWK SOD\EXI>EXIaVRPH6RXQGV>@@ 
aQRZ3OD\LQJUHOHDVHaQRZ3OD\LQJ 6\QWK SOD\EXI>EXIaVRPH6RXQGV>@@ 
aQRZ3OD\LQJUHOHDVH
IUHHWKHEXIIHUPHPRU\
aVRPH6RXQGV%XIIHUHGGR BIUHH 

Figure 3.7
Executing one line at a time.

3.2.3 Cue Players


Now lets turn to a more concrete example. Triggering sound les, a common technique when combining live performers with a tape part, is easily achieved in
SuperCollider. There are many approaches to the construction of cue players. These
range from a list of individual lines of code that you evaluate one by one during a
performance, to fully edged GUIs that completely hide the code from the user.
One question you need to ask is whether to play the sounds from RAM or stream
them from hard disk. The former is convenient for short les, and the latter for substantial cues that you wouldnt want to keep in RAM. There are several classes (both
in the standard distribution of SuperCollider and within extensions by third-party
developers) that help with these 2 alternatives. Heres a very simple example which
loads 2 les into RAM and plays them:
aP\%XIIHU %XIIHUUHDG VVRXQGVDZONZDY ORDGDVRXQG
aP\%XIIHUSOD\SOD\LWDQGQRWLFHLWZLOOUHOHDVHWKHQRGHDIWHU
SOD\LQJ

Buffers play method is really just a convenience method, though, and well probably want to do something fancier, such as fade in or out. Figure 3.7 presents an

92

Scott Wilson and Julio dEscrivn


6\QWK'HI SOD\EXI^DUJRXW EXIJDWH 

2XWDU RXW


3OD\%XIDU EXI%XI5DWH6FDOHNU EXI ORRS 


/LQHQNU JDWHGRQH$FWLRQ  


ZLWK
GRQH$FWLRQ
ZHUHOHDVHV\QWKZKHQIDGHLVGRQH
` DGG
aVRPH6RXQGV VRXQGV SDWK0DWFKFROOHFW^_SDWK_%XIIHUUHDG VSDWK `
Q DFRXQWHU
KHUH
VRXU*8,FRGH
Z :LQGRZQHZ 6LPSOH&XH3OD\HU5HFW  IURQW
ZYLHZGHFRUDWRU )ORZ/D\RXW ZYLHZERXQGV 
WKLVZLOOSOD\HDFKFXHLQWXUQ
%XWWRQQHZ Z5HFW  VWDWHVB >>3OD\&XH&RORUEODFN
&RORUFOHDU@@ DFWLRQB ^

LI QaVRPH6RXQGVVL]H^


LI Q ^aQRZ3OD\LQJUHOHDVH` 


aQRZ3OD\LQJ 6\QWK SOD\EXI>EXIaVRPH6RXQGV>Q@@ Q Q

` 
` 
WKLVVHWVWKHFRXQWHUWRWKHILUVWFXH
%XWWRQQHZ Z5HFW  VWDWHVB >>6WRS5HVHW&RORUEODFN
&RORUFOHDU@@ DFWLRQB ^Q aQRZ3OD\LQJUHOHDVH` 
IUHHWKHEXIIHUVZKHQWKHZLQGRZLVFORVHG
ZRQ&ORVH ^aVRPH6RXQGVGR BIUHH `

Figure 3.8
Playing cues with a simple GUI.

example which uses multiple cues in a particular order, played by executing the
code one line at a time. It uses the 3OD\%XI UGen, which you may remember from
chapter 1.
The middle 2 lines of the latter section of gure 3.7 consist of 2 statements, and
thus do 2 things when you press the enter key to execute. You can of course have
lines of many statements, which can all be executed at once. (Lines are separated by
carriage returns; statements, by semicolons.)
The 1 line at a time approach is good when developing something for yourself
or an SC-savvy user, but you might instead want something a little more elaborate
or user-friendly. Figure 3.8 is a simple example with a GUI.
SC also allows for streaming les in from disk using the 'LVN,Q and 9'LVN,Q
UGens (the latter allows for variable-speed streaming). There are also a number of

93

Composition with SuperCollider

third-party extension classes that do things such as automating the required housekeeping (e.g., Fredrik Olofssons 5HG'LVN,Q6DPSOHU).
The previous examples deal with mono les. For multichannel les (stereo being
the most common case) it is simplest to deal with interleaved les.2 Sometimes, however, you may need to deal with multiple mono cues. Figure 3.9 shows how to sort
them based on a folder containing subfolders of mono channels.
3.3

Generating Sound Material


The process of composition deals as much with creating sounds as it does with
ordering them. The ability to control sounds and audio processes at a low level can
be great for nding your own compositional voice. Again, an exhaustive discussion
of all of SuperColliders sound-generating capabilities would far exceed the scope of
this chapter, so well look at a few issues related to generating and capturing material
in SC and give a concrete example of an approach you might want to adapt for your
own purposes. As before, we will work here with sound les for the sake of convenience, but you should keep in mind that what were discussing could apply to more
or less any synthesis or processing technique.
3.3.1

Recording

At some point youre probably going to want to record SCs output for the purpose
of capturing a sound for further audio processing or assembly on a DAW, for
documenting a performance, or for converting an entire piece to a distributable
sound le format.
To illustrate this, lets make a sound by creating an effect that responds in an idiosyncratic way to the amplitude of an input le and then record the result. You may
not nd a commercial plug-in that will do this, but in SC, you should be able to do
what you can imagine (more or less!).
The 6HUYHU class provides easy automated recording facilities. Often, this is the
simplest way to capture your sounds. (See gure 3.10.)
After executing this, you should have a sound le in SCs recordings folder (see the
doc for platform-specic locations) labeled with the date and time SC began recording: SC_YYMMDD_HHMMSS.aif. 6HUYHU also provides handy buttons on the
Server window (appearance or availability varies by platform) to prepare, stop, and
start recording. On OSX it may look like this, or similar (see gure 3.11).
The above example uses the default recording options. Using the methods
SUHSDUH)RU5HFRUG SDWK , UHF&KDQQHOVB, UHF+HDGHU)RUPDWB, and UHF6DPSOH)RUPDWB,
you can customize the recording process. The latter 3 methods must be called before
SUHSDUH)RU5HFRUG. A common case is to change the sample format; the default is to

94

Scott Wilson and Julio dEscrivn

JDWKHUDOO\RXUIROGHUSDWKV
WKLVZLOOSDWKPDWFKHDFKIROGHULQWKHFROOHFWLRQLHZHZLOOKDYHDFROOHFWLRQ
RIFROOHFWLRQVRISDWKV
aJURXS2ILQGLY&XH)ROGHUV VRXQGV SDWK0DWFKFROOHFW^_LWHP_
LWHPDV6\PERO  SDWK0DWFK`
3RVWaJURXS2ILQGLY&XH)ROGHUVVHHWKHPDOO
FKHFNKRZPDQ\FXHV\RXZLOOKDYHLQWKHHQG
aJURXS2ILQGLY&XH)ROGHUVVL]H
DXWRPDWHWKHEXIIHULQJSURFHVVIRUDOOFXHV
aEXIIHUHG&XHV aJURXS2ILQGLY&XH)ROGHUVFROOHFW^_LWHPL_LWHPFROOHFW^_SDWK_
%XIIHUUHDG VSDWK ``QRZDOORXUFXHILOHVDUHVLWWLQJLQWKHLUEXIIHUV
aEXIIHUHG&XHV>@KHUHLVFXH
VHHLWLQWKHSRVWZLQGRZ
3RVWaEXIIHUHG&XHV>@
SOD\WKHPDOOLQD*URXSXVLQJRXUSUHYLRXVV\QWKGHI
ZHXVHELQGKHUHWRHQVXUHWKH\VWDUWVLPXOWDQHRXVO\

VELQG ^

aQRZ3OD\LQJ *URXSQHZ V DJURXSWRSXWDOOWKHFKDQQHOV\QWKVLQ

aEXIIHUHG&XHV>@GR ^_FXH_6\QWK SOD\EXI>EXIFXH@aQRZ3OD\LQJ `
` 

IDGHWKHPRXWWRJHWKHUE\VHQGLQJDUHOHDVHPHVVDJHWRWKHJURXS
aQRZ3OD\LQJUHOHDVH

Figure 3.9
Gathering up les for multichannel cues.

95

Composition with SuperCollider

VERRWPDNHVXUHWKHVHUYHULVUXQQLQJ
ILUVWHYDOXDWHWKLVVHFWLRQ
E %XIIHUUHDG VVRXQGVDZONZDY DVRXUFH
VSUHSDUH)RU5HFRUGSUHSDUHWKHVHUYHUWRUHFRUG \RXPXVWGRWKLVILUVW

 VLPXOWDQHRXVO\VWDUWWKHSURFHVVLQJDQGUHFRUGLQJ
VELQG ^

KHUH
VRXUIXQN\HIIHFW

[ ^YDUFROXPELDDPS


FROXPELD 3OD\%XIDU EORRS 


DPS $PSOLWXGHDU FROXPELD 
VWLFN\
DPSIROORZHU


2XWDU 5HVRQ]DU FROXPELDDPS ILOWHUIUHTIROORZVDPS


`SOD\
VUHFRUG
` 

VSDXVH5HFRUGLQJSDXVH
VUHFRUGVWDUWDJDLQ
VVWRS5HFRUGLQJVWRSUHFRUGLQJDQGFORVHWKHUHVXOWLQJVRXQGILOH

Figure 3.10
Recording the results of making sounds with SuperCollider.

Figure 3.11
A screen shot of a Server window.

96

Scott Wilson and Julio dEscrivn

record as 32-bit oating-point values. This has the advantage of tremendous dynamic range, which means you dont have to worry about clipping and can normalize later, but its not compatible with all audio software.
VUHF6DPSOH)RUPDWB LQW 

More elaborate recording can be realized, of course, by using the 'LVN2XW UGen.
Servers automatic functionality is in fact based on this. SC also has non-real-time
synthesis capabilities, which may be useful for rendering CPU-intensive code. (See
chapter 18.)
3.3.2

Thinking in the Abstract

Something that learners often nd difcult to do is to stop thinking about exactly


what they want to do at the moment, and instead consider whether the problem
theyre dealing with has a general solution. Generalizing your code can be very powerful. Imagine that we want to make a sound that consists of 3 bands of resonated
impulses. We might do something like this:

^
5HVRQ]DU 'XVWDU   
5HVRQ]DU 'XVWDU   
5HVRQ]DU 'XVWDU    UHFLSURFDOVFDOHWRHQVXUH
QRFOLSSLQJ
`SOD\

Now, through a bit of careful thinking, we can abstract the problem from this
concrete realization and come up with a more general solution:

I 
Q 
^
0L[ILOO Q^_L_5HVRQ]DU 'XVWDU  I  L  `
QUHFLSURFDOVFDOHWRHQVXUHQRFOLSSLQJ
`SOD\

This version has an equivalent result, but weve expressed it in terms of generalized
instructions. It shows you how to construct a Synth consisting of resonated impulses
tuned in whole-number ratios rather than as an exact arrangement of objects and
connections, as you might do in a visual patching language such as Max/MSP. Weve

97

Composition with SuperCollider

also used variables (f for frequency and n for number of resonators) to make our
code easy to change. This is the great power of abstraction: by expressing something
as a general solution, you can be much more exible than if you think in terms of
exact implementations. Now it happens that the example above is hardly shorter
than the rst, but look what we can do with it:

I 
Q 
^
0L[ILOO Q^_L_5HVRQ]DU 'XVWDU  I  L  `
QUHFLSURFDOVFDOHWRHQVXUHQRFOLSSLQJ
`SOD\

By changing I and Q were able to come up with a much more complex variant.
Imagine what the hard-coded version would look like with 50 individual 5HVRQ]
8*HQV typed out by hand. In this case, not only is the code more exible, its shorter;
and because of that, its much easier to understand. Its like the difference between
saying Make me 50 resonators and saying Make me a resonator. Make me a
resonator. Make me a resonator. . . .
This way of thinking has potential applications in almost every aspect of SC, even
GUI construction (see gure 3.12).
3.3.3

Gestures

For a long time, electroacoustic and electronic composition has been a rather
manual process. This may account for the computers being used today as a virtual analog studio; many sequencer software GUIs attest to this way of thinking.
However, as software has become more accessible, programming may in fact be replacing this virtual splicing approach.
One of the main advantages of a computer language is generalization, or abstraction, as we have seen above. In the traditional tape music studio approach, the
composer does not differentiate gesture from musical content. In fact, traditionally
they amount to much the same thing in electronic music. But can a musical gesture
exist independently of sound?
In electronic music, gestures are, if you will, the morphology of the sound, a compendium of its behavior. Can we take sound material and examine it under another
abstracted morphology? In ordinary musical terms this could mean a minor scale
can be played in crescendo or diminuendo and remain a minor scale. In electroacoustic music this can happen, for example, when we modulate 1 sound with the

98

Scott Wilson and Julio dEscrivn


I 
Q QXPEHURIUHVRQDWRUV
W $UUD\ILOO Q^_L_
^
5HVRQ]DU 'XVWDU  I  L 
QUHFLSURFDOVFDOHWRHQVXUHQRFOLSSLQJ
`SOD\
` 
QRZPDNHD*8,
DVFUROOLQJZLQGRZVRZHGRQ
WUXQRXWRIVSDFH
Z :LQGRZQHZ %XWWRQV5HFW  VFUROOWUXH 
ZYLHZGHFRUDWRU )ORZ/D\RXWQHZ ZYLHZERXQGV DXWROD\RXWWKHZLGJHWV
QGR ^_L_
%XWWRQQHZ Z5HFW  VWDWHVB >
>)UHT I  L 2Q&RORUEODFN&RORUZKLWH@
>)UHT I  L 2II&RORUZKLWH&RORUEODFN@
@
DFWLRQB ^DUJEXWW
W>L@UXQ EXWWYDOXH  
` 
` 
ZIURQW

Figure 3.12
A variable number of resonators with an automatically created GUI.

spectrum of another. The shape of 1 sound is generalized and applied to another; we


are accustomed to hearing this in signal-processing software. In this section we
would like to show how SuperCollider can be used to create empty gestures, gestures that are not linked to any sound in particular. They are, in a sense, gestures
waiting for a sound, abstractions of how to deliver the musical idea.
First we will look at some snippets of code that we can reuse in different patches,
and then we will look at some Routines we can call up as part of a Routine of Routines (i.e., a score, so to speak). If you prefer to work in a more traditional way, you
can just run the Routines with different sounds each time, record them to hard disk,
and then assemble or sample as usual in your preferred audio editing/sequencing
software. However, an advantage of doing the larger-scale organization of your
piece within SC is that since you are interpreting your code during the actual performance of your piece, you can add elements of variability to what is normally xed

99

Composition with SuperCollider

at the time of playback. You can also add elements of chance to your piece without
necessarily venturing fully into algorithmic composition. (Naturally, you can always
record the output to a sound le if desired.) This, of course, brings us back to issues
of design, and exactly what you choose to do will depend on your own needs and
inclinations.
3.3.4 Making Empty Gestures
Lets start by making a list where all our Buffers will be stored. This will come in
handy later on, as it will allow us to call up any le we opened with our le browser
during the course of our session. In the following example we open a dialogue box
and can select any sound(s) on our hard disk:
\RXZLOOEHDEOHWRDGGPXOWLSOHVRXQGILOHVMXVWVKLIWFOLFNZKHQ
VHOHFWLQJ
YDUILOHVRXQG3DWK
aEXIIHUV /LVW>@
'LDORJJHW3DWKV ^DUJSDWKV
SDWKVGR ^_VRXQG3DWK_
SRVWWKHSDWKWRYHULI\WKDWLWLVWKHRQH\RXH[SHFW
VRXQG3DWKSRVWOQ
DGGVWKHUHFHQWO\VHOHFWHG%XIIHUWR\RXUOLVW
aEXIIHUVDGG %XIIHUUHDG VVRXQG3DWK  `
` 

You can check to see how many Buffers are in your list so far (watch the post
window!),
aEXIIHUVVL]H

and you can see where each sound is inside your list. For example, here is the very
rst sound stored in our Buffer list:
aEXIIHUV>@

Now that we have our sound in a Buffer, lets try some basic manipulations. First,
lets just listen to the sound to verify that it is there:
aEXIIHUV>@SOD\

Now, lets make a simple 6\QWK'HI so we can create Synths which play our Buffer
(for example, in any 5RXWLQH, 7DVN, or other 6WUHDP) later on. For the purposes of this
demonstration we will use a very simple percussive envelope, making sure we have
GRQH$FWLRQ in order to free the synth after the envelope terminates:

100

Scott Wilson and Julio dEscrivn


EXIIHUSOD\HUZLWKGRQHDFWLRQDQGFRQWURORIHQYHORSHDQGSDQQLQJ
6\QWK'HI ?VDPSOH3OD\HU^DUJRXW EXI 
UDWH DW UHO SRV S6SHHG OHY 
YDUVDPSOHSDQ7DPSDX[
VDPSOH 3OD\%XIDU EXIUDWH %XI5DWH6FDOHNU EXI  
SDQ7 )6LQ2VFNU S6SHHG 
DPS (QY*HQDU (QYSHUF DWUHOOHY GRQH$FWLRQ 
2XWDU RXW3DQDU VDPSOHSDQ7DPS 
` DGG

As mentioned in chapter 1, we use the DGG method here rather than one of the
more low-level SynthDef methods such as VHQG. In addition to sending the def to the
server, DGG also stores it within the global 6\QWK'HVF/LE in the client app, so that its
arguments can be looked up later by the Patterns and Streams system (see chapter 6).
Well need this below. Lets test the SynthDef:
6\QWK ?VDPSOH3OD\HU>?RXW?EXIQXPaEXIIHUV>@?UHO@ 

As you can hear, it plays 0.25 second of the selected sound. Of course, if you have
made more than 1 Buffer list, you can play sounds from any list, and also play randomly from that list. For example, from the list we dened earlier we could do this:
6\QWK ?VDPSOH3OD\HU>?RXW?EXIQXPaEXIIHUVFKRRVH?UHO@ 

Lets dene a Routine that allows us to create a stuttering /rushing gesture in a


glitch style. Well use a new Pattern here, 3JHRP, which species a geometric series.3
Note that Patterns can be nested. Figure 3.13 shows a Pseq whose list consists of two
Pgeoms.
Remember that you can use a 7DVN or 5RXWLQH to sequence several such gestures
within your piece. You can, of course, modify the Routine to create other accel/decel
Patterns by substituting different Patterns. You can also add variability by making
some of them perform choices when they generate their values (e.g., using 3UDQG or
3[UDQG). You can use this, for example, to choose which speaker a sound comes from
without repeating speakers:
3[UDQG >@LQI

The advantage of having assigned your gestures to environment variables (using


the tilde shortcut) is that now you are able to experiment in real time with the ordering, simultaneity, and internal behavior of your gestures.
Lets take a quick look at 1 more important Pattern: 3ELQG. It creates a Stream
of Events, which are like a kind of dictionary of named properties and associated
values. If you send the message SOD\ to a Pbind, it will play the Stream of Events, in

101

Composition with SuperCollider

 DURXWLQHIRUFUHDWLQJDULWDUGDQGRVWXWWHUZLWKSDQQLQJ\RXPXVWKDYH
UXQWKHFRGHLQILJVRWKDWWKLVURXWLQHPD\ILQGVRPHVRXQGVDOUHDG\ORDGHG
LQWREXIIHUV\RXFDQFKDQJHWKHLQGH[RIaEXIIHUHG&XHVWRWHVWWKHURXWLQHRQ
GLIIHUHQWVRXQGV 

aVWXW 5RXWLQH ^YDUGXUSRV


aVWXW3DWW 3VHT >3JHRP  3Q  3JHRP  @ 
aVWU aVWXW3DWWDV6WUHDP
GR^
GXU aVWUQH[W
GXUSRVWOQ
VRZHFDQFKHFNYDOXHVRQWKHSRVWZLQGRZ
aVDPSOH 6\QWK VDPSOH3OD\HU>?RXW?EXIaEXIIHUHG&XHV>@?DW
?UHO?S6SHHG@ 
GXUZDLW
`
` 

QRZSOD\LW
aVWXWSOD\
UHVHWEHIRUH\RXSOD\DJDLQ
aVWXWUHVHW

Figure 3.13
Making a stuttering gesture using a geometric Pattern.

a fashion similar to the Clock examples above. Heres a simple example which makes
sound using whats called the default SynthDef:
UDQGRPO\VHOHFWHGIUHTXHQF\GXUDWLRQVHFRQG
3ELQG ?IUHT3UDQG >@ ?GXU SOD\

Its also possible to substitute Event Streams as they play. When you call SOD\ on
a 3DWWHUQ, it returns an (YHQW6WUHDP3OD\HU, which actually creates the individual
Events from the Stream dened by the Pattern. EventStreamPlayer allows its Stream
to be substituted while it is playing.
aJHVW 3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXU?UHO 
aSOD\HU aJHVWSOD\PDNHLWSOD\
aSOD\HUVWUHDP 3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXU?UDWH
3[UDQG >@LQI ?UHO DV6WUHDPVXEVWLWXWHWKH
VWUHDP
aSOD\HUVWRS

102

Scott Wilson and Julio dEscrivn

If you have evaluated the expressions above, you will notice that you dont hear
the simple default SynthDef, but rather the one we made earlier. Since we added it
above, the Pbind is able to look it up in the global library and get the information it
needs about the def. Now, the Pbind plays repeatedly at intervals specied by the
\dur argument, but it will stop playing as soon as it receives nil for this or any other
argument. So we can take advantage of this to make Streams that are not repetitive
and thus make single gestures (of course, we can also choose to work in a looping/
layering fashion, but more of that later). Here is a Pbind making use of our accelerando Pattern to create a rushing sound:
aJHVW 3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXU3JHRP  
?UHO 
aJHVWSOD\

When the Stream created from the Pgeom ended, it returned nil and the EventStreamPlayer stopped playing. If you call play on it again, you will notice that it
makes the same rushing sound without the need to reset it, as we had to do with the
Routine, since it will return a new EventStreamPlayer each time. More complex
gestures can be made, of course, by nesting patterns:
3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXU3VHT >3JHRP  
3JHRP  @ ?UHO?S6SHHG SOD\
3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXU3VHT >3JHRP  
3JHRP  @ ?UDWH3[UDQG >@LQI ?UHO
?S6SHHG SOD\

Similar things can be done with the 3GHI class from the -,7 library (see chapter 7).
Lets designate another environment variable to hold a sequence of values that we
can plug in at will and change on the y. This Pattern holds values that would work
well for \dur:
aUK\WKP 3VHT >QLO@ WKHQLOLVVRLWZLOO
VWRS

We can then plug it into a 3GHI, which well call ?D:


aJHVW 3GHI ?D3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXUaUK\WKP?UHO
?S6SHHG  
aJHVWSOD\

If we dene another sequence of values we want to try,


aUK\WKP 3VHT >
QLO@ 

103

Composition with SuperCollider

and then reevaluate the 3GHI,


aJHVW 3GHI ?D3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?GXUaUK\WKP?UHO
?S6SHHG  

we can hear that the new aUK\WKP has taken the place of the previous one. Notice
that it played immediately, without the need for executing aJHVWSOD\. This is one
of the advantages of working with the 3GHI class: once the 6WUHDP is running, anything that is poured into it will come out. In the following example, we assign a
Pattern to the rate values and obtain an interesting variation:
aJHVW 3GHI ?D3ELQG ?LQVWUXPHQW?VDPSOH3OD\HU?DWW?UHO
?OHY^UUDQG  `?GXU?UDWH3VHT >3EURZQ 
 @ 

Experiments like these can be conducted by creating Patterns for any of the arguments that our 6\QWK'HI will take. If we have added more than 1 6\QWK'HI, we can
even modulate the ?LQVWUXPHQW by getting it to choose among several different options. Once we have a set of gestures we like, we can trigger them in a certain order
using a 5RXWLQH, or we can record them separately and load them as audio les to our
audio editor. The latter approach is useful if we want to use a cue player for the nal
structuring of a piece.
3.4

Conclusions
What next? The best way to compose with SuperCollider is to set yourself a project
with a deadline! In this way you will come to grips with specic things you need to
know, and you will learn it much better than just by reviewing everything it can do.
SuperCollider offers a variety of approaches to electronic music composition. It can
be used for sound creation thanks to its rich offering of UGens (see chapter 2), as
well as for assembling your piece in exible ways. We have shown that the assembly
of sounds itself can become a form of synthesis, illustrated by our use of Patterns
and Streams. Another approach is to review some of the classic techniques used in
electroacoustic composition and try to re-create them yourself using SuperCollider.
Below we refer you to some interesting texts that may enhance your creative
investigations.

Further Reading
Budn, O. 2000. Composing with Objects, Networks, and Time Scales: An Interview with
Horacio Vaggione. Computer Music Journal, 24(3): 922.
Collins, N. 2010. Introduction to Computer Music. Chichester: Wiley.

104

Scott Wilson and Julio dEscrivn

Dodge, C., and T. A. Jerse. 1997. Computer Music: Synthesis, Composition, and Performance, 2nd ed. New York: Schirmer.
Holtzman, S. R. 1981. Using Generative Grammars for Music Composition. Computer
Music Journal, 5(1): 5164.
Loy, G. 1989. Composing with Computers: A Survey of Some Compositional Formalisms
and Music Programming Languages. In M. V. Mathews and J. R. Pierce, eds., Current Directions in Computer Music Research, pp. 291396. Cambridge, MA: MIT Press.
Loy, G., and Abbott, C. 1985. Programming Languages for Computer Music Synthesis,
Performance, and Composition. ACM Computing Surveys (CSUR), 17(2): 235265.
Mathews, M. V. 1963. The Digital Computer as a Musical Instrument. Science, 142(3592):
553557.
Miranda, E. R. 2001. Composing Music with Computers. London: Focal Press.
Roads, C. 2001. Microsound. Cambridge, MA: MIT Press.
Roads, C. 1996. The Computer Music Tutorial. Cambridge, MA: MIT Press.
Wishart, T. 1994. Audible Design: A Plain and Easy Introduction to Practical Sound Composition. York, UK: Orpheus the Pantomime.

Notes
1. You may have noticed that the terms message and method used somewhat interchangeably. In polymorphism the distinction becomes clear: different objects may respond to
the same message with different methods. In other words, the message is the command, and
the method is what the object does in response.
2. Scott Wilsons De-Interleaver application for OSX and Jeremy Friesners cross-platform
command line tools audio_combine and audio_split allow for convenient interleaving and
deinterleaving of audio les.
3. A geometric series is a series with a constant ratio between successive terms.

25

Writing Unit Generator Plug-ins


Dan Stowell

Writing a unit generator (UGen) for SuperCollider 3 can be extremely useful, allowing the addition of new audio generation and processing capabilities to the synthesis
server. The bulk of the work is C++ programming, but the API (Application Programming Interface) is essentially quite simple so even if you have relatively little
experience with C/C++, you can start to create UGens based on existing examples.
Youre probably already familiar with UGens from other chapters. Before creating
new UGens of your own, lets rst consider what a UGen really is, from the plug-in
programmers point of view.
25.1

What Is a UGen, Really?


A UGen is a component for the synthesis server, dened in a plug-in, which can receive a number of oating-point data inputs (audio- or control-rate signals or constant values) and produce a number of oating-point data outputs, as well as side
effects such as writing to the post window, accessing a buffer, or sending a message
over a network. The server can incorporate the UGen into a synthesis graph, passing
data from 1 UGen to another.
When using SC language, we need to have available a representation of each UGen
which provides information about its inputs and outputs (the number, type, etc.).
These representations allow us to dene synthesis graphs in SC language (SynthDefs). Therefore, each UGen also comes with an SC class; these classes are always
derived from a base class, appropriately called 8*HQ.
So to create a new UGen you need to create both the plug-in for the server and the
class le for the language client.

25.2

An Aside: Pseudo UGens


Before we create a real UGen, well look at something simpler. A pseudo UGen is
an SC class that behaves like a UGen from the users point of view but doesnt

692

Dan Stowell

involve any new plug-in code. Instead, it just encapsulates some useful arrangement
of existing units. Lets create an example, a simple reverb effect:
5HYHUE^
 DU^_LQ_
YDURXW LQ
RXW $OOSDVV1DU RXWUDQG 
ARXW
`
`

This isnt a very impressive reverb yet, but well improve it later.
As you can see, this is a class like any other, with a single class method. The DU
method name is not special in fact, you could use any method name (including
QHZ). We are free to use the full power of SC language, including constructs such as
UDQG, to choose a random delay time for our effect. The only real requirement
for a pseudo UGen is that the method returns something that can be embedded in a
synth graph. In our simple example, what is returned is an $OOSDVV1 applied to the
input.
Copy the above code into a new le and save it as, for instance, Reverb1.sc in your
SCClassLibrary or Extensions folder; then recompile. Youll now be able to use
5HYHUEDU within your SynthDefs, just as if it were a real UGen. Lets test this:
VERRW

[ ^
YDUIUHTVRQRXW
&KLUSVDWDUELWUDU\PRPHQWV
IUHT (QY*HQDU (QYSHUF  'XVWDU  
VRQ 6LQ2VFDU IUHT 
:HDSSO\UHYHUEWRWKHOHIWDQGULJKWFKDQQHOVVHSDUDWHO\
RXW ^5HYHUEDU VRQFXWRII `GXS
`SOD\ V 

[IUHH

You may wish to save this usage example as a rudimentary Help le, Reverb1.html.
To make the reverb sound more like a reverb, we modify it to perform 6 similar
all-pass delays in a row, and we also add some LPF units in the chain to create a nice
frequency roll-off. We also add parameters:
5HYHUE^
 DU^_LQZHW FXWRII _
YDURXW LQ
GR^RXW /3)DU $OOSDVV1DU RXWUDQG FXWRII `

693

25

Writing Unit Generator Plug-ins

A RXW ZHW  LQ  ZHW 


`
`

This is on the way toward becoming a useful reverb unit without having created a
real plug-in at all.
This approach has denite limitations. It is of course conned to processes that
can be expressed as a combination of existing units it cant create new types of
processing or new types of server behavior. It may also be less efcient than an
equivalent UGen, because it creates a small subgraph of units that pass data to each
other and must maintain their own internal states separately.
Now lets consider what is involved in creating a real UGen.
25.3

Steps Involved in Creating a UGen


1. First, consider exactly what functionality you want to encapsulate into a single
unit. An entire 808-drum machine, or just the cymbal sound? Smaller components
are typically better, because they can be combined in many ways within a SynthDef.
Efciency should also be a consideration.
2. Second, write the Help le. Really its a good idea to do this before you start
coding, even if you dont plan to release the UGen publicly.
As well as being a good place to keep the example code which you can use while
developing and testing the UGen, it forces you to think clearly about the inputs and
outputs and how the UGen will be used in practice, thus weeding out any conceptual
errors.
A Help le is also a good reminder of what the UGen does dont underestimate
the difculties of returning to your own code, months or years later, and trying to
decipher your original intentions!
The Help le will be an HTML le with the same name as the UGen. There is a
Documentation Style Guide in the SC Help system which includes tips and recommendations for writing Help documentation. But, of course, during development the
Help le doesnt need to be particularly beautiful.
3. Third, write the class le. You dont need to do this before starting on the C++
code, but its a relatively simple step. Existing class les (e.g., for SinOsc, LPF, Pitch,
Dwhite) can be helpful as templates. More on this shortly.
4. Fourth, write the plug-in code. The programming interface is straightforward,
and again existing plug-in code can be a helpful reference: all UGens are written
as plug-ins including the core UGens so there are lots of code examples
available.
We now consider writing the class le and writing the plug-in code.

694

25.4

Dan Stowell

Writing the Class File


A class le for a UGen is much like any other SC class, with the following conditions:
It must be a subclass of 8*HQ. This is so that methods dened in the 8*HQ class can be
used when the language builds the SynthDef (synth graph denition).
The name of the class must match the name used in the plug-in code the class name
is used to tell the server which UGen to instantiate.
It must implement the appropriate class methods for the rates at which it can run
(e.g., DU, NU, and/or LU). These method names are referenced for rate checking
during the SynthDef building process.
The class methods must call the PXOWL1HZ method (dened in the main 8*HQ class),
which processes the arguments and adds the UGen correctly to the SynthDef that is
being built.
The class le does not have any direct connection with the C++ plug-in code after
all, its the server that uses the plug-in code, while the class le is for the language
client.
Lets look at a well-known example:
6LQ2VF8*HQ^
 DU^
DUJIUHT SKDVH PXO DGG 
AWKLVPXOWL1HZ
DXGLR
IUHTSKDVH PDGG PXODGG
`
 NU^
DUJIUHT SKDVH PXO DGG 
AWKLVPXOWL1HZ
FRQWURO
IUHTSKDVH PDGG PXODGG
`
`

As you can see, 6LQ2VF is a subclass of 8*HQ and implements 2 class methods. Both
of these methods call PXOWL1HZ and return the result, which is 1 or more instances of
the UGen we are interested in. The methods also call PDGG, which well discuss
shortly.
The rst argument to PXOWL1HZ is a symbol to indicate the rate at which the
particular UGen instance will be operating: this could be DXGLR, FRQWURO,
VFDODU, or GHPDQG. The remaining arguments are those that will actually be
passed to the C++ plug-in here IUHT and SKDVH. If any of these arguments are arrays, PXOWL1HZ performs multichannel expansion, creating a separate unit to handle
each channel. Indeed, this is why the method is called PXOWL1HZ.
Note that the PXO and DGG arguments are not being passed in to PXOWL1HZ. This
means that the actual plug-in code for SinOsc will never be able to access them. In-

695

25

Writing Unit Generator Plug-ins

stead, this UGen makes use of the PDGG method, which is essentially a convenience
for multiplication and addition of the units output. As well as avoiding the programmers having to implement the multiplication and addition part of the process,
the PDGG method performs some general optimizations (e.g., in the very common
degenerate case of multiplying by 1 and adding 0; no processing is really required,
so the UGen is simply returned unaltered). It is the convention to add PXO and DGG
arguments to UGens as the nal 2 arguments, as is done here; these 2 arguments are
often very useful and are supported by many UGens. (Due to their commonness,
they are often undocumented in Help les.)
Lets start to draft the class le for a UGen we can implement. Well create a basic
anger which takes some input and then adds an effect controlled by rate and
depth parameters:
)ODQJHU8*HQ^
 DU^
DUJLQUDWH GHSWK PXO DGG 
AWKLVPXOWL1HZ
DXGLR
LQUDWHGHSWK PDGG PXODGG
`
 NU^
DUJLQUDWH GHSWK PXO DGG 
AWKLVPXOWL1HZ
FRQWURO
LQUDWHGHSWK PDGG PXODGG
`
`

Save this as Flanger.sc in your extensions directory. If you recompile, youll nd that
this is sufcient to allow you to use )ODQJHUDU or )ODQJHUNU in SynthDefs, which
the SuperCollider language will happily compile but of course those SynthDefs
wont run yet, because we havent created anything to tell the server how to produce
the Flanger effect.
25.4.1

Checking the Rates of Your Inputs

Because SuperCollider supports different signal rates, it is useful to add a bit of sanity checking to your UGen class to ensure that the user doesnt try to connect things
in a way that doesnt make sense: for example, plugging an audio-rate value into a
scalar-rate input.
The 8*HQ class provides a FKHFN,QSXWV method which you can override to perform
any appropriate checks. When the SynthDef graph is built, each UGens FKHFN,QSXWV
method will be called. The default method dened in 8*HQ simply passes through to
FKHFN9DOLG,QSXWV, which checks that each of the inputs is really something that can
be plugged into a synth graph (and not some purely client-side object such as, say, an
6&:LQGRZ or a 7DVN).

696

Dan Stowell

The %XI:U UGen is an example which implements its own rate checking. Lets look
at what the class does:
FKHFN,QSXWV^
LI UDWH 
DXGLR
DQG^LQSXWVDW  UDWH 
DXGLR
`^

A SKDVHLQSXWLVQRWDXGLRUDWHLQSXWVDW  LQSXWVDW  
UDWH 
` 
AWKLVFKHFN9DOLG,QSXWV
`

If %XI:U is used to write audio-rate data to a buffer, then the input specifying the
phase (i.e., the position at which data is written) must also be at audio rate theres
no natural way to map control-rate index data to a buffer which is taking audio-rate
data. Therefore the class overrides the FKHFN,QSXWV method to test explicitly for this.
The UDWH variable is the rate of the unit under consideration (a symbol, just like the
rst argument to PXOWL1HZ). The LQSXWV variable is an array of the units inputs, each
of which will be a UGen and thus will also have a UDWH variable. So the method compares the present units rate against its rst inputs rate. It simply returns a string
if theres a problem (returning anything other than nil is a sign of an error found
while checking input). If theres not a problem, then it passes through to the default
FKHFN9DOLG,QSXWV method if you implement your own method checking, dont
forget to pass through to this check.
Many UGens produce output at the same rate as their rst input for example,
lters such as /3) or +3). If you look at their class denition (or their superclass, in
the case of /3) and +3) an abstract class called )LOWHU), youll see that they call a
convenience method for this common case called FKHFN6DPH5DWH$V)LUVW,QSXW. Observe the result of these checks:
VERRW
[ ^/3)DU :KLWH1RLVHNU `SOD\ V (UURU
[ ^/3)DU :KLWH1RLVHDU `SOD\ V 2.
[IUHH
[ ^/3)NU :KLWH1RLVHDU `SOD\ V (UURU
[ ^/3)NU :KLWH1RLVHNU `SOD\ V 2.
[IUHH

What happens if you dont add rate checking to your UGens? Often it makes little
difference, but ignoring rate checking can sometimes lead to unusual errors that are
hard to trace. For example, a UGen that expects control-rate input is relatively safe,
because it expects less input data than an audio-rate UGen so if given audio-rate
data, it simply ignores most of it. But in the reverse case, a UGen that expects audiorate data but is given only control-rate data may read garbage input from memory
that it shouldnt be reading.

697

25

Writing Unit Generator Plug-ins

Returning to the )ODQJHU example created earlier, you may wish to add rate checking to that class. In fact, since the Flanger is a kind of lter, you might think it sensible to use the FKHFN6DPH5DWH$V)LUVW,QSXW approach, either directly or by modifying
the class so that it subclasses )LOWHU rather than 8*HQ.
25.5

Writing the C++ Code


25.5.1 Build Environments: Xcode, scons . . .
UGen plug-ins are built just like any other C++ project. To make things easier for
yourself as a developer, you can use and adapt 1 of the project les which are distributed along with SuperColliders source code:
On Mac, the Xcode project le Plugins.xcodeproj is used to build the core set of
SuperCollider plug-ins. Its relatively painless to add a new target to this project in
order to build your own plug-ins this is the approach used in the SuperCollider
Help document Writing Unit Generators, which has more details about the Xcode
specics.
On Linux, the scons project le SConstruct is used to build SuperCollider as a whole.
You can edit this le using a text editor to add your plug-ins build instructions. Alternatively, the sc3-plug-ins SourceForge project provides an SConstruct le
purely for building UGens you may nd it easier to start from that as a template.
On Windows, Visual Studio project les are provided to compile plug-ins, including
a UGEN_TEMPLATE_VCPROJ.vcprojtemplate le which you can use as a basis.
You can, of course, use other build environments if you prefer.
25.5.2 When Your Code Will Be Called
The server (scsynth) will call your plug-in code at 4 distinct points:
When scsynth boots, it calls the plug-ins ORDG function, which primarily declares
which UGens the plug-in can provide.
When a UGen is instantiated (i.e., when a synth starts playing), scsynth calls the
UGens constructor function to perform the setting up of the UGen.
To produce sound, scsynth calls each UGens calculation function in turn, once for
every control period. This is typically the function which does most of the interesting
work in the UGen. Since it is called only once during a control period, this function
must produce either a single control-rate value or a whole blocks worth of audiorate values during 1 call. (Note: Demand UGens dont quite t this description and
will be covered later.)

698

Dan Stowell

When a synth is ended, some UGens may need to perform some tidying up, such as
freeing memory. If so, these UGens provide a destructor function which is called at
this point.
25.5.3

The C++ Code for a Basic UGen

The code in gure 25.1 shows the key elements we need to include in our Flanger
plug-in code.
Here is what this code does:
First, the LQFOXGH command calls the main header le for SuperColliders plug-in
interface, SC_PlugIn.h. This is sufcient to include enough SuperCollider infrastructure for most types of UGen. (For phase vocoder UGens, more may be needed, as
described later.)
The static ,QWHUIDFH7DEOH pointer is a reference to a table of SuperCollider functions
such as the ones used to register a new UGen.
We dene a data structure (a struct) which will hold any data we need to store
during the operation of the UGen. This struct, which needs to be remembered or
passed from 1 audio block to the next, must be stored here. Note that the struct inherits from the base struct 8QLW this is necessary so that scsynth can correctly write
information into the struct, such as the rate at which the unit is running.
We declare our UGens functions, using the H[WHUQ& specier so that the scsynth
executable is able to reference the functions using C linkage. In a given plug-in we
are allowed to dene 1 or more UGens. Each of these will have 1 constructor
(Ctor) function, 1 or more calculation (next) functions, and optionally 1 destructor (Dtor) function.
Our constructor function, )ODQJHUB&WRU , takes a pointer to a Flanger struct and
must prepare the UGen for execution. It must do the following 3 things:
1. Initialize the Flanger structs member variables appropriately. In this case we initialize the GHOD\VL]H member to a value representing a 20-millisecond maximum
delay, making use of the 6$03/(5$7( macro which the SuperCollider API provides to
specify the sample rate for the UGen. For some of the other struct members, we wish
to calculate the values based on an input to the UGen. We can do this using the ,1
macro, which grabs a single control-rate value from the specied input. Here, we use
,1  remembering that numbering starts at 0, this corresponds to the second
input, dened in the Flanger class le as rate. These macros (and others) will be
discussed later.
2. Tell scsynth what the calculation function will be for this instance of the UGen.
The 6(7&$/& macro stores a reference to the function in our units struct. In our example theres only 1 choice, so we simply call 6(7&$/& )ODQJHUBQH[W . Its possible

699

25

Writing Unit Generator Plug-ins

LQFOXGH6&B3OXJ,QK
VWDWLF,QWHUIDFH7DEOH IW
WKHVWUXFWZLOOKROGGDWDZKLFKZHZDQWWRSDVVIURPRQHIXQFWLRQWRDQRWKHU
HJIURPWKHFRQVWUXFWRUWRWKHFDOFIXQF
RUIURPRQHFDOORIWKHFDOFIXQFWRWKHQH[W
VWUXFW)ODQJHUSXEOLF8QLW^

IORDWUDWHGHOD\VL]HIZGKRSUHDGSRV

LQWZULWHSRV
`
IXQFWLRQGHFODUDWLRQVH[SRVHGWR&
H[WHUQ&^

YRLGORDG ,QWHUIDFH7DEOH LQ7DEOH 

YRLG)ODQJHUB&WRU )ODQJHU XQLW 

YRLG)ODQJHUBQH[W )ODQJHU XQLWLQWLQ1XP6DPSOHV 
`

YRLG)ODQJHUB&WRU )ODQJHU XQLW ^




+HUHZHPXVWLQLWLDOLVHVWDWHYDULDEOHVLQWKH)ODQJHUVWUXFW

XQLW!GHOD\VL]H 6$03/(5$7( I)L[HGPVPD[GHOD\

7\SLFDOO\ZLWKUHIHUHQFHWRFRQWUROUDWHVFDODUUDWHLQSXWV

IORDWUDWH ,1  

5DWKHUWKDQXVLQJUDWHGLUHFWO\ZH
UHJRLQJWRFDOFXODWHWKHVL]HRI

MXPSVZHPXVWPDNHHDFKWLPHWRVFDQWKURXJKWKHGHOD\OLQHDWUDWH

IORDWGHOWD  XQLW!GHOD\VL]H UDWH 6$03/(5$7(

XQLW!IZGKRS GHOWDI

XQLW!UDWH UDWH







`

,03257$177KLVWHOOVVFV\QWKWKHQDPHRIWKHFDOFXODWLRQIXQFWLRQ
IRUWKLV8*HQ
6(7&$/& )ODQJHUBQH[W 
6KRXOGDOVRFDOFVDPSOH
VZRUWKRIRXWSXW
HQVXUHVHDFKXJHQ
VSLSHVDUHSULPHG
)ODQJHUBQH[W XQLW 

Figure 25.1
C++ code for a Flanger UGen. This code doesnt add any effect to the sound yet, but contains
the key elements required for all UGens.

700

Dan Stowell

YRLG)ODQJHUBQH[W )ODQJHU XQLWLQWLQ1XP6DPSOHV ^





IORDW LQ ,1  


IORDW RXW 287  

IORDWGHSWK ,1  







IORDWUDWH
IORDWIZGKRS
IORDWUHDGSRV
LQWZULWHSRV
LQWGHOD\VL]H

IORDWYDOGHOD\HG




IRU LQWL LLQ1XP6DPSOHVL ^



YDO LQ>L@







'RVRPHWKLQJWRWKHVLJQDOEHIRUHRXWSXWWLQJ
 QRW\HWGRQH





`

RXW>L@ YDO



`

XQLW!ZULWHSRV ZULWHSRV
XQLW!UHDGSRV UHDGSRV

XQLW!UDWH
XQLW!IZGKRS
XQLW!UHDGSRV
XQLW!ZULWHSRV
XQLW!GHOD\VL]H

YRLGORDG ,QWHUIDFH7DEOH LQ7DEOH ^




IW LQ7DEOH


`

'HILQH6LPSOH8QLW )ODQJHU 

Figure 25.1
(continued)

701

25

Writing Unit Generator Plug-ins

to dene multiple calculation functions and allow the constructor to decide which
one to use. This is covered later.
3. Calculate one samples worth of output, typically by calling the units calculation
function and asking it to process 1 sample. The purpose of this is to prime the
inputs and outputs of all the unit generators in the graph and to ensure that the constructors for UGens farther down the chain have their input values available so they
can initialize correctly.
Our calculation function, )ODQJHUBQH[W , should perform the main audio processing. In this example it doesnt actually alter the sound well get to that shortly but
it illustrates some important features of calculation functions. It takes 2 arguments
passed in by the server: a pointer to the struct and an integer specifying how many
values are to be processed (this will be 1 for control-rate, more for audio-rate
typically 64).
The last thing in our C++ le is the ORDG function, called when the scsynth executable boots up.
We store the reference to the interface table which is passed in note that although
you dont see any explicit references to IW elsewhere in the code, thats because they
are hidden behind macros which make use of it to call functions in the server.
We must also declare to the server each of the UGens which our plug-in denes. This
is done using a macro 'HILQH6LPSOH8QLW )ODQJHU , which tells the server to register
a UGen with the name Flanger and with a constructor function named Flanger_Ctor.
It also tells the server that no destructor function is needed. If we did require a destructor, we would instead use 'HILQH'WRU8QLW )ODQJHU , which tells the server that
weve also supplied a destructor function named Flanger_Dtor. You must name your
constructor/destructor functions in this way, since the naming convention is hardcoded into the macros.
So what is happening inside our calculation function? Although in our example the
input doesnt actually get altered before being output, the basic pattern for a typical
calculation function is given. We do the following:
Create pointers to the input and output arrays which we will access: IORDW LQ 
The macros ,1 and 287 return appropriate pointers for the desired inputs/outputs in this case the rst input and the rst output. If
the input is audio-rate, then LQ>@ will refer to the rst incoming sample, LQ>@ to
the next incoming sample, and so on. If the input is control-rate, then there is only 1
incoming value, LQ>@.
We use the macro ,1 again to grab a single control-rate value, here the depth
input. Note that ,1 is actually a shortcut to the rst value in the location referenced by ,1 . ,1  is exactly the same as ,1  >@.
,1  IORDW RXW 287  ;

702

Dan Stowell

We copy some values from the UGens struct into local variables. This can improve
the efciency of the unit, since the C++ optimizer will typically cause the values to be
loaded into registers.
Next we loop over the number of input frames, each time taking an input value,
processing it, and producing an output value. We could take values from multiple
inputs, and even produce multiple outputs, but in this example were using only 1
full-rate input and producing a single output. Two important notes:
If an input/output is control-rate and you mistakenly treat it as audio-rate, you will
be reading/writing memory you should not be, and this can cause bizarre problems and crashes; essentially this is just the classic C/C++ gotcha of accidentally
treating an array as being bigger than it really is. Note that in our example, we
assume that the input and output are of the same size, although its possible that
they arent some UGens can take audio-rate input and produce control-rate
output. This is why it is useful to make sure your SuperCollider class code includes
the rate-checking code described earlier in this chapter. You can see why the
FKHFN6DPH5DWH$V)LUVW,QSXW approach is useful in this case.
The server uses a buffer coloring algorithm to minimize use of buffers and to
optimize cache performance. This means that any of the output buffers may be
the same as 1 of the input buffers. This allows for in-place operation, which is
very efcient. You must be careful, however, not to write any output sample
before you have read the corresponding input sample. If you break this rule, then
the input may be overwritten with output, leading to undesired behavior. If you
cant write the UGen efciently without breaking this rule, then you can instruct
the server not to alias the buffers by using the 'HILQH6LPSOH&DQW$OLDV8QLW or
'HILQH'WRU&DQW$OLDV8QLW macros in the ORDG function, rather than the
'HILQH6LPSOH8QLW or 'HILQH'WRU8QLW macros. (The Help le on writing UGens
provides an example in which this ordering is important.)
Finally, having produced our output, we may have modied some of the variables we
loaded from the struct; we need to store them back to the struct so the updated values are used next time. Here we store the UDWH value back to the struct although we
dont modify it in this example, we will shortly change the code so that this may
happen.
The code in gure 25.1 should compile correctly into a plug-in. With the class le in
place and the plug-in compiled, you can now use the UGen in a synth graph:
VERRW

[ ^
YDUVRQGO\RXW
VRQ 6DZDU >@ PHDQ
RXW )ODQJHUDU VRQ 

703

25

Writing Unit Generator Plug-ins

RXWGXS 
`SOD\ V 

Remember that Flanger doesnt currently add any effect to the sound. But we can at
least check that it runs correctly (outputting its input unmodied and undistorted)
before we start to make things interesting.
25.5.4

Summary: The Three Main Rates of Data Output

Our example has taken input in 3 different ways:


Using ,1 in the constructor to take an input value and store it to the struct for
later use. Since this reads a value only once, the input is being treated as a scalar-rate
input.
Using ,1 in the calculation function to take a single input value. This treats the
input as control-rate.
Using ,1 in the calculation function to get a pointer to the whole array of inputs.
This treats the input as audio-rate. Typically the size of such an input array is accessed using the LQ1XP6DPSOHV argument, but note that if you create a control-rate
UGen with audio-rate inputs, then LQ1XP6DPSOHV will be wrong (it will be 1), so you
should instead use the macro )8//%8)/(1*7+ (see table 25.2).
If the data that one of your UGens inputs is fed is actually audio-rate, there is no
danger in treating it as control-rate or scalar-rate. The end result is to ignore the
extra data provided to your UGen. Similarly, a control-rate input can safely be
treated as scalar-rate. The result would be crude downsampling without low-pass
ltering, which may be undesirable but will not crash the server.
25.5.5

Allocating Memory and Using a Destructor

Next we can develop our Flanger example so that it applies an effect to the sound.
In order to create a anging effect, we need a short delay line (around 20 milliseconds). We vary the amount of delay and mix the delayed sound with the input
to produce the effect.
To create a delay line, we need to allocate some memory and store a reference to
that memory in the UGens data structure. And, of course, we need to free this
memory when the UGen is freed. This requires a UGen with a destructor. Figure 25.2
shows the full code, with the destructor added, as well as the code to allocate, free,
and use the memory. Note the change in the ORDG function we use 'HILQH'WRU
8QLW rather than 'HILQH6LPSOH8QLW . (Weve also added code to the calculation
function which reads and writes to the delay line, creating the anging effect.)

704

Dan Stowell

LQFOXGH6&B3OXJ,QK
VWDWLF,QWHUIDFH7DEOH IW
WKHVWUXFWZLOOKROGGDWDZKLFKZHZDQWWRSDVVIURPRQHIXQFWLRQWRDQRWKHU
HJIURPWKHFRQVWUXFWRUWRWKHFDOFIXQF
RUIURPRQHFDOORIWKHFDOFIXQFWRWKHQH[W
VWUXFW)ODQJHUSXEOLF8QLW^

IORDWUDWHGHOD\VL]HIZGKRSUHDGSRV

LQWZULWHSRV


DSRLQWHUWRWKHPHPRU\ZH
OOXVHIRURXULQWHUQDOGHOD\

IORDW GHOD\OLQH
`
IXQFWLRQGHFODUDWLRQVH[SRVHGWR&
H[WHUQ&^

YRLGORDG ,QWHUIDFH7DEOH LQ7DEOH 

YRLG)ODQJHUB&WRU )ODQJHU XQLW 

YRLG)ODQJHUBQH[W )ODQJHU XQLWLQWLQ1XP6DPSOHV 

YRLG)ODQJHUB'WRU )ODQJHU XQLW 
`

YRLG)ODQJHUB&WRU )ODQJHU XQLW ^














+HUHZHPXVWLQLWLDOLVHVWDWHYDULDEOHVLQWKH)ODQJHUVWUXFW
XQLW!GHOD\VL]H 6$03/(5$7( I)L[HGPVPD[GHOD\
7\SLFDOO\ZLWKUHIHUHQFHWRFRQWUROUDWHVFDODUUDWHLQSXWV
IORDWUDWH ,1  
5DWKHUWKDQXVLQJUDWHGLUHFWO\ZH
UHJRLQJWRFDOFXODWHWKHVL]HRI
MXPSVZHPXVWPDNHHDFKWLPHWRVFDQWKURXJKWKHGHOD\OLQHDWUDWH
IORDWGHOWD  XQLW!GHOD\VL]H UDWH 6$03/(5$7(
XQLW!IZGKRS GHOWDI
XQLW!UDWH UDWH
XQLW!ZULWHSRV 
XQLW!UHDGSRV 


$OORFDWHWKHGHOD\OLQH

XQLW!GHOD\OLQH  IORDW 57$OORF XQLW!P:RUOGXQLW!GHOD\VL]H 
VL]HRI IORDW 

,QLWLDOLVHLWWR]HURHV

Figure 25.2
Completed C++ code for the Flanger UGen.

705

25

Writing Unit Generator Plug-ins

PHPVHW XQLW!GHOD\OLQHXQLW!GHOD\VL]H VL]HRI IORDW 








`

,03257$177KLVWHOOVVFV\QWKWKHQDPHRIWKHFDOFXODWLRQIXQFWLRQ
IRUWKLV8*HQ
6(7&$/& )ODQJHUBQH[W 
6KRXOGDOVRFDOFVDPSOH
VZRUWKRIRXWSXW
HQVXUHVHDFKXJHQ
VSLSHVDUHSULPHG
)ODQJHUBQH[W XQLW 

YRLG)ODQJHUBQH[W )ODQJHU XQLWLQWLQ1XP6DPSOHV ^





IORDW LQ ,1  


IORDW RXW 287  

IORDWGHSWK ,1  








IORDWUDWH XQLW!UDWH
IORDWIZGKRS XQLW!IZGKRS
IORDWUHDGSRV XQLW!UHDGSRV
IORDW GHOD\OLQH XQLW!GHOD\OLQH
LQWZULWHSRV XQLW!ZULWHSRV
LQWGHOD\VL]H XQLW!GHOD\VL]H

IORDWYDOGHOD\HGFXUUDWH

FXUUDWH ,1  










LI UDWH FXUUDWH ^

UDWHLQSXWQHHGVXSGDWLQJ

UDWH FXUUDWH

IZGKRS  GHOD\VL]H UDWH  6$03/(5$7( I
`











IRU LQWL LLQ1XP6DPSOHVL ^



YDO LQ>L@
:ULWHWRWKHGHOD\OLQH
GHOD\OLQH>ZULWHSRV@ YDO
LI ZULWHSRV GHOD\VL]H

ZULWHSRV 

Figure 25.2
(continued)

706

Dan Stowell





















5HDGIURPWKHGHOD\OLQH
GHOD\HG GHOD\OLQH> LQW UHDGSRV@
UHDGSRV IZGKRS
8SGDWHSRVLWLRQ1%ZHPD\EHPRYLQJIRUZDUGVRUEDFNZDUGV
 GHSHQGLQJRQLQSXW
ZKLOH LQW UHDGSRV! GHOD\VL]H

UHDGSRV GHOD\VL]H
ZKLOH LQW UHDGSRV

UHDGSRV GHOD\VL]H







`

0L[GU\DQGZHWWRJHWKHUDQGRXWSXWWKHP
RXW>L@ YDO GHOD\HG GHSWK 





`

XQLW!UDWH UDWH
XQLW!IZGKRS IZGKRS
XQLW!ZULWHSRV ZULWHSRV
XQLW!UHDGSRV UHDGSRV

YRLG)ODQJHUB'WRU )ODQJHU XQLW ^



57)UHH XQLW!P:RUOGXQLW!GHOD\OLQH 
`
YRLGORDG ,QWHUIDFH7DEOH LQ7DEOH ^


IW LQ7DEOH


`

'HILQH'WRU8QLW )ODQJHU 

Figure 25.2
(continued)

707

25

Writing Unit Generator Plug-ins

Table 25.1
Memory Allocation and Freeing
Typical C Allocation/Freeing

In SuperCollider (using the real-time pool)

YRLG SWU PDOORF QXPE\WHV


IUHH SWU

YRLG SWU 57$OORF XQLW!P:RUOGQXPE\WHV


57)UHH XQLW!P:RUOGSWU

SuperCollider UGens allocate memory differently from most programs. Ordinary memory allocation and freeing can be a relatively expensive operation, so
SuperCollider provides a real-time pool of memory from which UGens can borrow
chunks in an efcient manner. The functions to use in a plug-in are in the right-hand
column of table 25.1, and the analogous functions (the ones to avoid) are shown in
the left-hand column.
57$OORF and 57)UHH can be called anywhere in your constructor/calculation/
destructor functions. Often you will 57$OORF the memory during the constructor and
57)UHH it during the destructor, as is done in gure 25.2.
Memory allocated in this way is taken from the (limited) real-time pool and is not
accessible outside the UGen (e.g., to client-side processes). If you require large
amounts of memory or wish to access the data from the client, you may prefer to use
a buffer allocated and then passed in from outside this is described later.
25.5.6

Providing More Than 1 Calculation Function

Your UGens choice of calculation function is specied within the constructor rather
than being xed. This gives an opportunity to provide different functions optimized
for different situations (e.g., 1 for control-rate and 1 for audio-rate input) and to
decide which to use. This code, used in the constructor, would choose between 2
calculation functions according to whether the rst input was audio-rate or not:
LI ,15$7(   FDOFB)XOO5DWH ^
6(7&$/& )ODQJHUBQH[WBD 
`HOVH^
6(7&$/& )ODQJHUBQH[WBN 
`

You would then provide both a )ODQJHUBQH[WBD and a )ODQJHUBQH[WBN function.


Similarly, you could specify different calculation functions for audio-rate versus
control-rate output (e.g., by testing whether %8)/(1*7+ is 1; see table 25.2), although
this is often catered for automatically when your calculation function uses the
LQ1XP6DPSOHV argument to control the number of loops performed, and so on.

708

Dan Stowell

Table 25.2
Useful Macros for UGen Writers
Macro

Description

,1(index)

A oat* pointer to input number index

287(index)

A oat* pointer to output number index

,1(index)

A single (control-rate) value from input number


index

287(index)

A single (control-rate) value at output number


index

,15$7((index)

The rate of input index, an integer value


corresponding to 1 of the following constants:
FDOFB6FDODU5DWH (scalar-rate)
FDOFB%XI5DWH (control-rate)
FDOFB)XOO5DWH (audio-rate)
FDOFB'HPDQG5DWH (demand-rate)

6(7&$/&(func)

Set the calculation function to func

6$03/(5$7(

The sample rate of the UGen as a double. Note: for


control-rate UGens this is not the full audio rate
but audio rate/blocksize)

6$03/('85

Reciprocal of 6$03/(5$7( (seconds per sample)

%8)/(1*7+

Equal to the block size if the unit is audio rate and


to 1 if the unit is control rate

%8)5$7(

The control rate as a double

%8)'85

The reciprocal of %8)5$7(

*(7%8)

Treats the UGens rst input as a reference to a


buffer; looks this buffer up in the server, and
provides variables for accessing it, including
IORDW EXI'DWD, which points to the data; XLQW
EXI)UDPHV for how many frames the buffer
contains; XLQWEXI&KDQQHOV for the number of
channels in the buffer

&OHDU8QLW2XWSXWV(unit,

A function which sets all the units outputs to 0

inNumSamples)
3ULQW(fmt, ...)

Print text to the SuperCollider post window;


arguments are just like those for the C function
printf

'RQH$FWLRQ(doneAction, unit)

Perform a doneAction, as used in EnvGen,


DetectSilence, and others

57$OORF(world, numBytes)

Allocate memory from the real-time pool


analogous to malloc(numBytes)

575HDOORF(world, pointer,

Reallocate memory in the real-time pool


analogous to realloc(pointer, numBytes)

numBytes)

709

25

Writing Unit Generator Plug-ins

Table 25.2
(continued)
Macro

Description

57)UHH(world, pointer)

Free allocated memory back to the real-time pool


analogous to free(pointer)

6HQG7ULJJHU(node, triggerID, value)

Send a trigger from the node to clients, with integer


ID, triggered, and oat value value

)8//5$7(

The full audio sample rate of the server (irrespective


of the rate of the UGen) as a double

)8//%8)/(1*7+

The integer number of samples in an audio-rate


input (irrespective of the rate of the UGen)

The units calculation function can also be changed during execution the
6(7&$/& macro can safely be called from a calculation function, not just from the
constructor. Whenever you call 6(7&$/& , this changes which function the server
will call, from the next control period onward.
The Help le on writing UGens shows more examples of 6(7&$/& in use.
25.5.7

Trigger Inputs

Many UGens make use of trigger inputs. The convention here is that if the input is
nonpositive (i.e., 0 or negative), then crosses to any positive value, a trigger has occurred. If you wish to provide trigger inputs, use this same convention.
The change from nonpositive to positive requires checking the trigger inputs value
against its previous value. This means that our struct will need a member to store the
previous value for checking. Assuming that our struct contains a oat member
SUHYWULJ, the following sketch outlines how we handle the incoming data in our
calculation function:
RDWWULJ ,1  2UZKLFKHYHULQSXW\RXZLVK
RDWSUHYWULJ XQLW!SUHYWULJ
LI SUHYWULJ  WULJ! ^
GRVRPHWKLQJ
`
XQLW!SUHYWULJ WULJ6WRUHFXUUHQWYDOXHQH[WWLPHLW
OOEHWKH
SUHYLRXVYDOXH

The sketch is for a control-rate trigger input, but a similar approach is used for audio-rate triggering, too. For audio-rate triggering, you need to compare each value
in the input block against the value immediately previous. Note that for the very rst
value in the block, you need to compare against the last value from the previous
block (which you must have stored).

710

Dan Stowell

For complete code examples, look at the source of the 7ULJ UGen, found in
TriggerUGens.cpp in the main SC distribution.
25.5.8 Accessing a Buffer
When a buffer is allocated and then passed in to a UGen, the UGen receives the index number of that buffer as a oat value. In order to get a pointer to the correct
chunk of memory (as well as the size of that chunk), the UGen must look it up in the
servers list of buffers.
In practice this is most easily achieved by using a macro called *(7B%8). You can
call *(7B%8) near the top of your calculation function, and then the data are available via a oat pointer EXI'DWD along with 2 integers dening the size of the buffer,
EXI&KDQQHOV and EXI)UDPHV. Note that the macro assumes the buffer index is the rst
input to the UGen (this is the case for most buffer-using UGens).
For examples which use this approach, look at the code for the 'LVN,Q or 'LVN2XW
UGens, dened in DiskIO_UGens.cpp in the main SC distribution.
Your UGen does not need to free the memory associated with a buffer once
it ends. The memory is managed externally by the buffer allocation/freeing server
commands.
25.5.9

Randomness

The API provides a convenient interface for accessing good-quality pseudo-random


numbers. The randomness API is specied in SC_RGen.h and provides functions for
random numbers from standard types of distribution: uniform, exponential, bilinear, and quasi-Gaussian (such as VXPUDQG, also available client-side). The server
creates an instance of the random number generator for UGens to access. The following excerpt shows how to generate random numbers for use in your code:
5*HQ UJHQ  XQLW!P3DUHQW!P5*HQ
RDWU UJHQIUDQG $UDQGRPRDWXQLIRUPO\GLVWULEXWHGWR

LQWUYDO UJHQLUDQG  $UDQGRPLQWHJHUXQLIRUPO\GLVWULEXWHG
WRLQFOXVLYH
RDWUJDXV UJHQVXPUDQG  4XDVL*DXVVLDQOLPLWHGWRUDQJH

25.5.10

When Your UGen Has No More to Do

Many UGens carry on indenitely, but often a UGen reaches the end of its useful
life (e.g., it nishes outputting an envelope or playing a buffer). There are 3 specic behaviors that might be appropriate if your UGen does reach a natural end:

711

25

Writing Unit Generator Plug-ins

1. Some UGens set a done ag to indicate that theyve nished. Other UGens can
monitor this and act in response to it (e.g., 'RQH, )UHH6HOI:KHQ'RQH). See the Help
les for examples of these UGens. If you wish your UGen to indicate that it has nished, set the ag as follows:
XQLW!P'RQH WUXH

This doesnt affect how the server treats the UGen the calculation function will still
be called in future.
2. UGens such as (QY*HQ, /LQHQ, 'XW\, and /LQH provide a doneAction feature
which can perform actions such as freeing the node once the UGen has reached the
end of its functionality. You can implement this yourself simply by calling the
'RQH$FWLRQ macro, which performs the desired action. You would typically allow
the user to specify the doneAction as an input to the unit. For example, if the
doneAction is the sixth input to your UGen, you would call
'RQH$FWLRQ ,1  XQLW

Since this can perform behaviors such as freeing the node, many UGens stop calculating/outputting after they reach the point of calling this macro. See, for example,
the source code for 'HWHFW6LOHQFH, which sets its calculation function to a no-op
'HWHFW6LOHQFHBGRQH function at the point where it calls 'RQH$FWLRQ. Not all
GRQH$FWLRQV free the synth, though, so additional output is not always redundant.
3. If you wish to output zeroes from all outputs of your unit, you can simply call the
&OHDU8QLW2XWSXWV function as follows:
&OHDU8QLW2XWSXWV XQLWLQ1XP6DPSOHV 

Notice that this function has the same signature as a calculation function: as arguments it takes a pointer to the unit struct and an integer number of samples. You
can take advantage of this similarity to provide an efcient way to stop producing
output:
6(7&$/& &OHDU8QLW2XWSXWV 

Calling this would mean that your calculation function would not be called in future
iterations. Instead, &OHDU8QLW2XWSXWV would be called. Therefore this provides an
irreversible but efcient way for your UGen to produce silent output for the remainder of the synths execution.
25.5.11

Summary of Useful Macros

Table 25.2 summarized some of the most generally useful macros dened for use in
your UGen code. Many of these are discussed in this chapter, but not all are covered
explicitly. The macros are dened in SC_Unit.h and SC_InterfaceTable.h.

712

25.6

Dan Stowell

Specialized Types of UGen


25.6.1 Multiple-Output UGens
In the C++ code, writing UGens which produce multiple outputs is very straightforward. The 287 macro gets a pointer to the desired-numbered output. Thus, for a
3-output UGen, assign each one (287  , 287  , 287  ) to a variable, then write
output to these 3 pointers.
In the SuperCollider class code, the default is to assume a single output, and we
need to modify this behavior. Lets look at the 3LWFK UGen to see how its done:
3LWFK0XOWL2XW8*HQ^
 NU^DUJLQ LQLW)UHT PLQ)UHT PD[)UHT 
H[HF)UHT PD[%LQV3HU2FWDYH PHGLDQ 
DPS7KUHVKROG SHDN7KUHVKROG GRZQ6DPSOH 
AWKLVPXOWL1HZ
FRQWURO
LQLQLW)UHTPLQ)UHTPD[)UHTH[HF)UHT

PD[%LQV3HU2FWDYHPHGLDQDPS7KUHVKROGSHDN7KUHVKROGGRZQ6DPSOH
`
LQLW^DUJWKH,QSXWV
LQSXWV WKH,QSXWV
AWKLVLQLW2XWSXWV UDWH 
`
`

There are 2 differences from an ordinary UGen. First, 3LWFK is a subclass of


0XOWL2XW8*HQ rather than of 8*HQ; 0XOWL2XW8*HQ takes care of some of the changes
needed to work with a UGen with multiple outputs. Second, the LQLW function is
overridden to say exactly how many outputs this UGen will provide (in this case, 2).
For 3LWFK, the number of outputs is xed, but in some cases it might depend on
other factors. 3OD\%XI is a good example of this: its number of outputs depends on
the number of channels in the EXIIHU(s) it is expecting to play, specied using the
QXP&KDQQHOV argument. The init method for 3OD\%XI takes the QXP&KDQQHOV input
(i.e., the rst value from the list of inputs passed to init) and species that as the
number of outputs.
25.6.2

Passing Arrays into UGens

25.6.2.1 The class le


As described earlier, the PXOWL1HZ method automatically performs multichannel expansion if any of the inputs are arrays yet in some cases we want a single unit to
handle a whole array, rather than having 1 unit per array element. The %XI:U and
5HFRUG%XI UGens are good examples of UGens that do exactly this: each UGen can

713

25

Writing Unit Generator Plug-ins

take an array of inputs and write them to a multichannel buffer. Heres how the class
le handles this:
5HFRUG%XI8*HQ^

DU^DUJLQSXW$UUD\EXIQXP RIIVHW UHF/HYHO 
SUH/HYHO UXQ ORRS WULJJHU 

AWKLVPXOWL1HZ/LVW >
DXGLR
EXIQXPRIIVHWUHF/HYHOSUH/HYHO
UXQORRSWULJJHU@LQSXW$UUD\DV$UUD\ 
`
`

Instead of calling the 8*HQ method PXOWL1HZ, we call PXOWL1HZ/LVW, which is the same
except that all the arguments are a single array rather than a separated argument list.
This means that the LQSXW$UUD\ argument (which could be either a single unit or an
array), when concatenated onto the end of the argument list using the  array concatenation operator, in essence appears as a set of separate input arguments rather
than a single array argument.
Note that 5HFRUG%XI doesnt know in advance what size the input array is going to
be. Because of the array attening that we perform, this means that the 5HFRUG%XI
C++ plug-in receives a variable number of inputs each time it is instantiated. Our
plug-in code will be able to detect how many inputs it receives in a given instance.
Why do we put LQSXW$UUD\ at the end of the argument list? Why not at the beginning, in parallel with how a user invokes the 5HFRUG%XI UGen? The reason is to make
things simpler for the C++ code, which will access the plug-in inputs according to
their numerical position in the list. The UHF/HYHO input, for example, is always the
third input, whereas if we inserted LQSXW$UUD\ into the list before it, its position
would depend on the size of LQSXW$UUD\.
The 3ROO UGen uses a very similar procedure, converting a string of text into
an array of ASCII characters and appending them to the end of its argument list.
However, the 3ROO class code must perform some other manipulations, so it is perhaps less clear as a code example than 5HFRUG%XI. But if you are developing a UGen
that needs to pass text data to the plug-in, 3ROO shows how to do it using this array
approach.
25.6.2.2 The C++ code
Ordinarily we access input data using the ,1 or ,1 macro, specifying the number of the input we want to access. Arrays are passed into the UGen as a separate
numeric input for each array element, so we access these elements in exactly the
same way. But we need to know how many items to expect, since the array can be of
variable size.
The 8QLW struct can tell us how many inputs in total are being provided (the member XQLW!P1XP,QSXWV. Look again at the 5HFRUG%XI class code given above. There

714

Dan Stowell

are 7 ordinary inputs, plus the array appended to the end. Thus the number of
channels in our input array is XQLW!P1XP,QSXWV . We use this information to
iterate over the correct number of inputs and process each element.
25.6.3

Demand-Rate UGens

25.6.3.1 The class le


Writing the class le for a demand-rate UGen is straightforward. Look at the code
for units such as 'VHULHV, 'JHRP, or 'ZKLWH as examples. They differ from other
UGen class les in 2 ways:
1. The rst argument to PXOWL1HZ (or PXOWL1HZ/LVW) is
GHPDQG
.
2. They implement a single class method, QHZ, rather than DU/ NU/ LU. This is because although some UGens may be able to run at multiple rates (e.g., audio rate or
control rate), a demand-rate UGen can run at only 1 rate: the rate at which data are
demanded of it.
25.6.3.2 The C++ code
The C++ code for a demand-rate UGen works as normal, with the constructor specifying the calculation function. However, the calculation function behaves slightly
differently.
First, it is not called regularly (once per control period) but only when demanded,
which during a particular control period could be more than once or not at all. This
means that you cant make assumptions about regular timing, such as the assumptions made in an oscillator which increments its phase by a set amount each time it
is called.
Second, rather than being invoked directly by the server, the calculation function
calls are actually passed up the chain of demand-rate generators. Rather than using
the ,1 or ,1 macros to access an input value (whose generation will have been
coordinated by the server), we instead use the '(0$1',1387 macro, which requests
a new value directly from the unit farther up the chain, on demand.
Note: because of the method used to demand the data, demand-rate UGens are
currently restricted to being single-output.
25.6.4

Phase Vocoder UGens

Phase vocoder UGens operate on frequency-domain data stored in a buffer (produced by the ))7 UGen). They dont operate at a special rate of their own: in reality they are control-rate UGens. They produce and consume a control-rate signal
which acts as a type of trigger: when an FFT frame is ready for processing, its value

715

25

Writing Unit Generator Plug-ins

is the appropriate buffer index; otherwise, its value is 1. This signal is often referred
to as the chain in SC documentation.
25.6.4.1 The class le
As with demand-rate UGens, phase vocoder UGens (PV UGens) can have only a
single rate of operation: the rate at which FFT frames are arriving. Therefore, PV
UGens implement only a single QHZ class method, and they specify their rate as
control in the call to PXOWL1HZ. See the class les for 39B0DJ0XO and 39B%ULFN:DOO
as examples of this.
PV UGens process data stored in buffers, and the C++ API provides some useful
macros to help with this. The macros assume that the rst input to the UGen is the
one carrying the FFT chain where data will be read and then written, so it is sensible
to stick with this convention.
25.6.4.2 The C++ code
PV UGens are structured just like any other UGen, except that to access the
frequency-domain data held in the external buffer, there are certain macros and procedures to use. Any of the core UGens implemented in PV_UGens.cpp should serve
as a good example to base your own UGens on. Your code should include the header
le FFT_UGens.h, which denes some PV-specic structs and macros.
Two important macros are 39B*(7B%8) and 39B*(7B%8), one of which you use at
the beginning of your calculation function to obtain the FFT data from the buffer.
These macros implement the special PV UGen behavior: if the FFT chain has red,
then they access the buffer(s) and continue with the rest of the calculation function;
but if the FFT chain has not red in the current control block, then they output a
value of 1 and return (i.e., they do not allow the rest of the calculation function to
proceed). This has the important consequence that although your calculation function code will look as if it is called once per control block, in fact your code will
be executed only at the FFT frame rate.
39B*(7B%8) will take the FFT chain indicated by the rst input to the UGen and create a pointer to these data called EXI.
39B*(7B%8) is for use in UGens which process 2 FFT chains and write the result
back out to the rst chain: it takes the FFT chain indicated by the rst and second
inputs to the UGen and creates pointers to the data called EXI and EXI.
It should be clear that you use 39B*(7B%8) or 39B*(7B%8), but not both.

Having acquired a pointer to the data, you will of course wish to read/write that
data. Before doing so, you must decide whether to process the complex-valued data
as polar coordinates or Cartesian coordinates. The data in the buffer may be in

716

Dan Stowell

either format (depending on what has happened to it so far). To access the data as
Cartesian values you use
6&&RPSOH[%XI S 7R&RPSOH[$S[ EXI 

and to access the data as polar values you use


6&3RODU%XI S 7R3RODU$S[ EXI 

These 2 data structures, and the 2 functions for obtaining them, are declared in
FFT_UGens.h. The name S for the pointer is of course arbitrary, but its what well
use here.
FFT data consist of a complex value for each frequency bin, with the number of
bins related to the number of samples in the input. But in the SuperCollider context
the input is real-valued data, which means that (a) the bins above the Nyquist frequency (which is half the sampling frequency) are a mirror image of the bins below,
and can therefore be neglected; and (b) phase is irrelevant for the DC and Nyquist
frequency bins, so these 2 bins can be represented by a single-magnitude value rather
than a complex value.
The end result of this is that we obtain a data structure containing a single DC
value, a single Nyquist value, and a series of complex values for all the bins in between. The number of bins in between is given by the value QXPELQV, which is provided for us by 39B*(7B%8) or 39B*(7B%8). The data in a Cartesian-type struct (an
6&&RPSOH[%XI) are of the form
S!GF
S!ELQ>@UHDO
S!ELQ>@LPDJ
S!ELQ>@UHDO
S!ELQ>@LPDJ

S!ELQ>QXPELQV@UHDO
S!ELQ>QXPELQV@LPDJ
S!Q\T

The data in a polar-type struct (an 6&3RODU%XI) is of the form


S!GF
S!ELQ>@PDJ
S!ELQ>@SKDVH
S!ELQ>@PDJ
S!ELQ>@SKDVH

S!ELQ>QXPELQV@PDJ
S!ELQ>QXPELQV@SKDVH
S!Q\T

717

25

Writing Unit Generator Plug-ins

Note that the indexing is slightly strange: engineers commonly refer to the DC component as the rst bin in the frequency-domain data. However in these structs,
because the DC component is represented differently, ELQ>@ is actually the rst nonDC bin what would sometimes be referred to as the second bin. Similarly, keep in
mind that QXPELQV represents the number of bins not including the DC or Nyquist
bins.
To perform a phase vocoder manipulation, simply read and write to the struct
(which actually is directly in the external buffer). The buffer will then be passed
down the chain to the next phase vocoder UGen. You dont need to do anything
extra to output the frequency-domain data.
When compiling your PV UGen, you will need to compile/link against SCComplex
.cpp from the main SuperCollider source, which provides the implementation of
these frequency-domain data manipulations.
25.7

Practicalities
25.7.1

Debugging

Standard C++ debugging procedures can be used when developing UGens. The simplest method is to add a line into your code which prints out values of variables you
can use the standard C++ SULQWI method, which in a UGen will print text to the
post window.
For more power, you can launch the server process, then attach a debugger such
as gdb (the GNU debugger) or Xcodes debugger (which is actually gdb with a
graphical interface) to perform tasks such as pausing the process and inspecting values of variables. On Mac, if you use the debugger to launch SuperCollider.app, remember that the local server runs in a process different from the application. You
can either launch the application using the debugger and booting the internal server,
or you can launch just the server (scsynth) using the debugger, which then runs as a
local server. In the latter case you need to ensure your debugger launches scsynth
with the correct arguments (e.g., X).
When debugging a UGen that causes server crashes, you may wish to look at your
systems crash log for scsynth. The most common cause of crashes is introduced
when using 57$OORF and 57)UHH if you try to 57)UHH something that has not yet
been 57$OORFed, or otherwise is not a pointer to the real-time memory pool, this can
cause bad-access exceptions to appear in the crash log. If the crash log seems to reveal that your UGen is somehow causing crashes inside core UGens which normally
behave perfectly, then check that your code does not write data outside of the expected limits: make sure you 57$OORF the right amount of space for what youre

718

Dan Stowell

doing (for example, with arrays, check exactly which indices your code attempts to
access).
25.7.2

Optimization

Optimizing code is a vast topic and often depends on the specics of the code in
question. However, we can suggest some optimization tips for writing SuperCollider
UGens. The efciency/speed of execution is usually the number-one priority, especially since a user may wish to employ many instances of the UGen simultaneously.
The difference between a UGen that takes 2.5% and another that takes 1.5% CPU
may seem small, but the rst limits you to 40 simultaneous instances, while the second will allow up to 66; a 65% increase. Imagine doing your next live performance
on a 4-year-old processor thats essentially the effect of the less efcient code.
Avoid calls to expensive procedures whenever possible. For example, oatingpoint division is typically much more expensive than multiplication, so if your unit
must divide values by some constant value which is stored in your struct, rewrite this
so that the reciprocal of that value is stored in the struct and you can perform a
multiplication rather than a division. If you want to nd an integer power of 2, use
bit shifting (  Q) rather than the expensive math function (SRZ  Q ). Other
expensive oating-point operations are square-root nding and trigonometric operations (VLQ, FRV, WDQ, etc.). Precalculate and store such values wherever possible,
rather than calculating them afresh every time the calculation function is called.
As a typical example, often a lter UGen will take a user parameter (such as cutoff
frequency) and use it to derive internal lter coefcients. If you store the previous
value of the user parameter and use this to check whether it has changed at all
updating the coefcients only upon a change you can improve efciency, since often UGens are used with xed or rarely changing parameters.
One of the most important SuperCollider-specic choices is, for reading a certain
input or even performing a given calculation, whether to do this at scalar/control/
audio rate. It can be helpful to allow any and all values to be updated at audio rate,
but if you nd that a certain update procedure is expensive and wont usually be
required to run at audio rate, it may be preferable to update only once during a calculation function.
Creating multiple calculation functions, each appropriate to a certain context (e.g.,
to a certain combination of input rates, as demonstrated earlier), and choosing the
most appropriate, can allow a lot of optimization. For example, a purely controlrate calculation can avoid the looping required for audio-rate calculation and typically produces a much simpler calculation as a result. There is a maintenance
overhead in providing these alternatives, but the efciency gains can be large. In this

719

25

Writing Unit Generator Plug-ins

tension between efciency and code comprehensibility/reusability, you should remember the importance of adding comments to your code to clarify the ow and the
design decisions you have made.
In your calculation function, store values from your struct as well as input/output
pointers/values as local variables, especially if referring to them multiple times. This
avoids the overhead of indirection and can be optimized (by the compiler) to use
registers better.
Avoid 'HILQH6LPSOH&DQW$OLDV8QLW and 'HILQH'WRU&DQW$OLDV8QLW. As described earlier, 'HILQH6LPSOH&DQW$OLDV8QLW is available as an alternative to 'HILQH6LPSOH8QLW
in cases where your UGen must write output before it has read from the inputs, but
this can decrease cache performance.
Avoid peaky CPU usage. A calculation function that does nothing for the rst 99
times its called, then performs a mass of calculations on the 100th call, could cause
audio dropouts if this spike is very large. To avoid this, amortize your units effort
by spreading the calculation out, if possible, by precalculating some values which are
going to be used in that big 100th call.
On Mac, Apples vDSP library can improve speed by vectorizing certain calculations.
If you make use of this, or other platform-specic libraries, remember the considerations of platform independence. For example, use preprocessor instructions to
choose between the Mac-specic code and ordinary C++ code:
LI6&B'$5:,1
7KH0DFVSHFLFYHUVLRQRIWKHFRGH LQFOXGLQJHJY'63IXQFWLRQV
HOVH
7KHJHQHULFYHUVLRQRIWKHFRGH
HQGLI

is a preprocessor value set to 1 when compiling SuperCollider on Mac


(this is set in the Xcode project settings). Branching like this introduces a maintenance overhead, because you need to make sure that you update both branches in
parallel.

6&B'$5:,1

25.7.3

Distributing Your UGens

Sharing UGens with others contributes to the SuperCollider community and is a very
cool thing to do. A SourceForge project, sc3-plug-ins, exists as a repository for
downloadable UGen plug-ins produced by various people. You may wish to publish
your work either there or separately.
Remember that SuperCollider is licensed under the well-known GPL (GNU Public
License) open-source license, including the plug-in API. So if you wish to distribute your plug-ins to the world, they must also be GPL-licensed. (Note: you retain

720

Dan Stowell

copyright in any code you have written. You do not have to sign away your copyright in order to GPL-license a piece of code.) Practically, this has a couple of implications:
You should include a copyright notice, a copy of the GPL license text, and the
source code with your distributed plug-in.
If your plug-in makes use of third-party libraries, those libraries must be available
under a GPL-compatible copyright license. See the GNU GPL Web site for further
discussion of what this means.

25.8

Conclusion
This chapter doesnt offer an exhaustive list of all thats possible, but it provides you
with the core of what all UGen programmers need to know. If you want to delve
deeper, you will nd the online community to be a valuable resource for answers to
questions not covered here; and the source code for existing UGens provides a wealth
of useful code examples.
The open-source nature of SuperCollider makes for a vibrant online developer
community. Whether you are tweaking 1 of SuperColliders core UGens or developing something very specialized, youll nd the exchange of ideas with SuperCollider
developers can be rewarding for your own projects as well as for others and can feed
into the ongoing development of SuperCollider as a uniquely powerful and exible
synthesis system.

26

Inside scsynth
Ross Bencina

This chapter explores the implementation internals of scsynth, the server process of
SuperCollider 3, which is written in C++. This chapter is intended to be useful to
people who are interested in modifying or maintaining the scsynth source code and
also to those who are interested in learning about the structure and implementation
details of one of the great milestones in computer music software. By the time youve
nished this chapter, you should have improved your understanding of how scsynth
does what it does and also have gained some insight into why it is written the way it
is. In this chapter sometimes well simply refer to scsynth as the server. The client usually refers to sclang or any other program sending OSC commands to the
server. Although the text focuses on the servers real-time operating mode, the information presented here is equally relevant to understanding scsynths non-real-time
mode. As always, the source code is the denitive reference and provides many interesting details which space limitations didnt allow to be included here.
Wherever possible, the data, structure, and function names used in this chapter
match those in the scsynth source code. However, at the time of writing there was
some inconsistency in class and structure naming. Sometimes you may nd that the
source le, the class name, or both may have an SC_ prex. I have omitted such
prexes from class and function names for consistency.
Also note that I have chosen to emphasize an object-oriented interpretation of
scsynth using UML diagrams to illuminate the code structure, as I believe scsynth is
fundamentally object-oriented, if not in an idiomatically C++ way. In many cases
structs from the source code appear as classes in the diagrams. Where appropriate, I
have taken the liberty to interpret inheritance where a base struct is included as the
rst member of a derived struct. However, I have resisted the urge to translate any
other constructs (such as the psuedo member functions mentioned below). All other
references to names appear here as they do in the source code.
Now that formalities are completed, in the next section we set out on our journey
through the scsynth implementation with a discussion of scsynths coding style. Following that, we consider the structure of the code which implements what I call the

722

Ross Bencina

scsynth domain model: Nodes, Groups, Graphs, GraphDefs, and their supporting
infrastructure. We then go on to consider how the domain model implementation
communicates with the outside world; we consider threading, interthread communications using queues, and how scsynth fullls real-time performance constraints
while executing all of the dynamic behavior offered by the domain model. The nal
section briey highlights some of the ne-grained details which make scsynth one of
the most efcient software synthesizers on the planet. scsynth is a fantastically elegant and interesting piece of software; I hope you get as much out of reading this
chapter as I did in writing it!
26.1

Some Notes on scsynth Coding Style


scsynth is coded in C++, but for the most part uses a C++ as a better C coding
style. Most data structures are declared as plain old C structs, especially those which
are accessible to unit plug-ins. Functions which in idiomatic C++ might be considered member functions are typically global functions in scsynth. These are declared
with names of the form 6WUXFW7\SHB0HPEHU)XQFWLRQ1DPH 6WUXFW7\SH V>@ ,
where the rst parameter is a pointer to the struct being operated on (the this
pointer in a C++ class). Memory allocation is performed with custom allocators or
with PDOORF , IUHH , and friends. Function pointers are often used instead of virtual functions. A number of cases of what can be considered inheritance are implemented by placing an instance of the base class (or struct) as the rst member of the
derived struct. There is very little explicit encapsulation of data using getter/setter
methods.
There are a number of pragmatic reasons to adopt this style of coding. Probably
the most signicant is the lack of an Application Binary Interface (ABI) for C++,
which makes dynamically linking with plug-ins using C++ interfaces compilerversion-specic. The avoidance of C++ constructs also has the benet of making all
code operations visible, in turn making it easier to understand and predict the performance and real-time behavior of the code.
The separation of data from operations and the explicit representation of operations as data-using function pointers promotes a style of programming in which
types are composed by parameterizing structs by function pointers and auxilliary
data. The use of structs instead of C++ classes makes it less complicated to place
objects into raw memory. Reusing a small number of data structures for many purposes eases the burden on memory allocation by ensuring that dynamic objects belong to only a small number of size classes. Finally, being able to switch function
pointers at runtime is a very powerful idiom which enables numerous optimizations,
as will be seen later.

723

26.2

26

Inside scsynths

The scsynth Domain Model


At the heart of scsynth is a powerful yet simple domain model which manages dynamic allocation and evaluation of unit generator graphs in real time. Graphs can be
grouped into arbitrary trees whose execution and evaluation order can be dynamically modied (McCartney, 2000). In this section we explain the main behaviors and
relationships between entities in the domain model. The model is presented without
concern for how client communication is managed or how the system is executed
within real-time constraints. These concerns are addressed in later sections.
Figure 26.1 shows an implementation-level view of the signicant domain entities
in scsynth. Each class shown on the diagram is a C++ class or struct in the scsynth
source code. SC users will recognize the concepts modeled by many of these classes.
Interested readers are advised to consult the ServerArchitecture section of the
Help les for further information about the roles of these classes and the exact operations which can be performed by them.
:RUOG is the top-level class which (with the exception of a few global objects) aggregates and manages the run-time data in the server. It is created by :RUOGB1HZ
when scsynth starts up. An instance of :RUOG2SWLRQV is passed to :RUOGB1HZ . It
stores the conguration parameters, which are usually passed to scsynth on the command line.
scsynths main task is to synthesize and process sound. It does this by evaluating a
tree of dynamically allocated 1RGH instances (near middle-left of gure 26.1), each of
which provides its own 1RGH&DOF)XQF function pointer, which is called by the server
to evaluate the Node at the current time step. 1RGHP,' is an integer used by clients
to identify specic Nodes in server commands (such as suspending or terminating
the Node, or changing its location in the tree).
There are 2 subtypes of 1RGH: *UDSK and *URXS. *UDSK is so named because it executes an optimized graph of UGens. It can be likened to a voice in a synthesizer or
an instrument in a Music N-type audio synthesis language such as Csound. The
*UDSK type implements the SuperCollider concept of a Synth. *URXS is simply a container for a linked list of 1RGH instances, and since *URXS is itself a type of 1RGH, arbitrary trees may be constructed containing any combination of *URXS and *UDSK
instances; readers may recognize this as the Composite design pattern (Gamma et al.,
1995). The standard 1RGH&DOF)XQF for a Group (*URXSB&DOF in 6&B*URXSFSS) simply iterates through the Groups contained Nodes, calling each Nodes 1RGH&DOF)XQF
in turn. Although most code deals with Nodes polymorphically, the 1RGHP,V*URXS
eld supports discriminating between Nodes of type *UDSK and of *URXS at runtime.
Any node can be temporarily disabled using the QBUXQ server command, which
switches NodeCalcFuncs. When a Node is switched off, a 1RGH&DOF)XQF which does

724

Ross Bencina

Figure 26.1
Class diagram of signicant domain entities.

725

26

Inside scsynths

nothing is substituted for the usual one. Disabling a Group disables the whole tree
under that Group.
A *UDSK is an aggregate of interconnected 8QLW subclasses (also known as Unit
Generators or UGens). 8QLW instances are responsible for performing primitive audio
DSP operations such as mixing, ltering, and oscillator signal generation. Each *UDSK
instance is carved out of a single memory block to minimize the number of expensive
calls to the memory allocator. Units are efciently allocated from the Graphs memory block and evaluated by iterating through a linear array containing pointers to all
of the Graphs Units. Each 8QLW instance provides a 8QLW&DOF)XQF function pointer
to compute samples, which affords the same kind of exibility as 1RGH&DOF)XQF described above. For example, many Units implement a form of self-modifying code by
switching their UnitCalcFuncs on the y to execute different code paths, depending
on their state.
Graphs are instantiated using a *UDSK'HI (Graph Denition), which denes the
structure of a class of Graphs. The *UDSK'HI type implements the SuperCollider concept of a SynthDef. A *UDSK'HI includes both data for passive representation (used
on disk and as communicated from clients such as sclang), and optimized in-memory
information used to efciently instantiate and evaluate Graphs. *UDSK'HI instances
store data such as memory allocation size for *UDSK instances, Unit initialization
parameters, and information about the connections between Units. When a new
GraphDef is loaded into the server, most of the work is done in *UDSK'HIB5HDG ,
which converts the stored representation to the run-time representation. Aside from
allocating and initializing memory and wiring in pointers, one of the main tasks
*UDSK'HIB5HDG performs is to determine which inter-Unit memory buffers will be
used to pass data between Units during Graph evaluation.
The stored GraphDef representation species an interconnected graph of named
8QLW instances with generalized information about input and output routing. This
information is loaded into an in-memory array of 8QLW6SHF instances where each
Unit name is resolved to a pointer to a 8QLW'HI (see below), and the Unit interconnection graph is represented by instances of ,QSXW6SHF and 2XWSXW6SHF. This interconnection graph is traversed by a graph-coloring algorithm to compute an allocation
of inter-Unit memory buffers, ensuring that the minimum number of these buffers is
used when evaluating the Graph. Note that the order of Unit evaluation dened by
a GraphDef is not modied by scsynth.
scsynths tree of Nodes is rooted at a Group referenced by :RUOGP7RS*URXS.
:RUOG is responsible for managing the instantiation, manipulation, and evaluation of
the tree of Nodes. :RUOG also manages much of the servers global state, including the
buses used to hold control and audio input and output signals (e.g., :RUOGP$XGLR%XV)
and a table of 6QG%XI instances (aka Buffers) used, for example, to hold sound data
loaded from disk. An instance of :RUOG is accessible to 8QLW plug-ins via 8QLWP:RUOG

726

Ross Bencina

and provides :RUOGIW, an instance of ,QWHUIDFH7DEOH, which is a table of function


pointers which Units can invoke to perform operations on the World. An example of
Units using World state is the ,Q and 2XW units which directly access :RUOGP$XGLR%XV
to move audio data between Graphs and the global audio buses.
8QLW subclasses provide all of the signal-processing functionality of scsynth. They
are dened in dynamically loaded executable plug-ins. When the server starts, it
scans the nominated plug-in directories and loads each plug-in, calling its ORDG
function; this registers all available Units in the plug-in with the World via the
,QWHUIDFH7DEOHI'HILQH8QLW function pointer. Each call to I'HILQH8QLW results
in a new 8QLW'HI being created and registered with the global J8QLW'HI/LE hash
table, although this process is usually simplied by calling the macros dened in
6&B,QWHUIDFH7DEOHK, such as 'HILQH6LPSOH8QLW and 'HILQH'WRU8QLW .
Some server data (more of which we will see later) is kept away from Unit plug-ins
in an instance of +LGGHQ:RUOG. Of signicance here are +LGGHQ:RUOGP1RGH/LE, a
hash table providing fast lookup of Nodes by integer ID; +LGGHQ:RUOGP*UDSK'HI/LE,
a hash table of all loaded GraphDefs, which is used when a request to instantiate a
new Graph is received; and +LGGHQ:RUOGP:LUH%XI6SDFH, which contains the memory used to pass data between Units during Graph evaluation.
26.3

Real-Time Implementation Structure


We now turn our attention to the context in which the server is executed. This includes considerations of threading, memory allocation, and interthread communications. scsynth is a real-time system, and the implementation is signicantly inuenced
by real-time requirements. We begin by considering what real-time requirements
means in the context of scsynth and then explore how these requirements are met.
26.3.1

Real-Time Requirements

scsynths primary responsibility is to compute blocks of audio data in a timely manner in response to requests from the OS audio service. In general, the time taken to
compute a block of audio must be less than the time it takes to play it. These blocks
are relatively small (on the order of 2 milliseconds for current generation systems),
and hence tolerances can be quite tight. Any delay in providing audio data to the OS
will almost certainly result in an audible glitch.
Of course, computing complex synthesized audio does not come for free and necessarily takes time. Nonetheless, it is important that the time taken to compute each
block is bounded and as close to constant as possible, so that exceeding timing constraints occurs only due to the complexity or quantity of concurrently active Graphs,
not to the execution of real-time unsafe operations. Such unsafe operations include

727

26

Inside scsynths

Algorithms with high or unpredictable computational complexity (for example,


amortized time algorithms with poor worst-case performance)
Algorithms which intermittently perform large computations (for example, precomputing a lookup table or zeroing a large memory block at Unit startup)
Operations which block or otherwise cause a thread context switch.

The third category includes not only explicit blocking operations, such as attempting to lock a mutex or wait on a le handle, but also operations which may block
due to unknown implementation strategies, such as calling a system-level memory
allocator or writing to a network socket. In general, any system call should be considered real-time unsafe, since there is no way to know whether it will acquire a lock
or otherwise block the process.
Put simply, no real-time unsafe operation may be performed in the execution context which computes audio data in real time (usually a thread managed by the OS
audio service). Considering the above constraints alongside the dynamic behavior
implied by the domain model described in the previous section and the fact that scsynth can read and write sound les on disk, allocate large blocks of memory, and
communicate with clients via network sockets, you may wonder how scsynth can
work at all in real time. Read on, and all will be revealed.
26.3.2

Real-Time Messaging and Threading Implementation

SuperCollider carefully avoids performing operations which may violate real-time


constraints by using a combination of the following techniques:
Communication to and from the real-time context is mediated by lock-free First In
First Out (FIFO) queues containing executable messages
Use of a xed-pool memory allocator which is accessed only from the real-time
context
Non-real-time safe operations (when they must be performed at all) are deferred
and executed asynchronously in a separate non-real-time thread
Algorithms which could introduce unpredictable or transient high computational
load are generally avoided
Use of user-congurable nonresizable data structures. Exhaustion of such data
structures typically results in scsynth operations failing.

The rst point is possibly the most important to grasp, since it denes the pervasive mechanism for synchronization and communication between non-real-time
threads and the real-time context which computes audio samples. When a non-realtime thread needs to perform an operation in the real-time context, it enqueues a
message which is later performed in the real-time context. Conversely, if code in the

728

Ross Bencina

real-time context needs to execute a real-time unsafe operation, it sends the message
to a non-real-time thread for execution. We will revisit this topic on a number of
occasions throughout the remainder of the chapter.
Figure 26.2 shows another view of the scsynth implementation, this time focusing
on the classes which support the real-time operation of the server. For clarity, only
a few key classes from the domain model have been retained (shaded gray). Note
that $XGLR'ULYHU is a base class: in the implementation different subclasses of
$XGLR'ULYHU are used depending on the target OS (CoreAudio for Mac OS X,
PortAudio for Windows, etc.).
Figure 26.3 illustrates the run-time thread structure and the dynamic communication pathways between threads via lock-free FIFO message queues. The diagram can
be interpreted as follows: thick rectangles indicate execution contexts, which are
either threads or callbacks from the operating system. Cylinders indicate FIFO message queue objects. The padlock indicates a lock (mutex), and the black circle indicates a condition variable. Full arrows indicate synchronous function calls (invocation
of queue-member functions), and half arrows indicate the ow of asynchronous
messages across queues.
The FIFO message queue mechanism will be discussed in more detail later in the
chapter, but for now, note that the :ULWH method enqueues a message, 3HUIRUP
executes message-specic behavior for each pending message, and )UHH cleans up
after messages which have been performed. The :ULWH , 3HUIRUP , and )UHH
FIFO operations can be safely invoked by separate reader and writer threads without the use of locks.
Referring to gures 26.2 and 26.3, the dynamic behavior of the server can be summarized as follows:
1. One or more threads listen to network sockets to receive incoming OSC messages which contain commands for the server to process. These listening threads
dynamically allocate 26&B3DFNHWV and post them to The Engine, using the
3URFHVV26&3DFNHW function, which results in 3HUIRUPB7R(QJLQHB0VJ (a
)LIR0VJ)XQF) being posted to the P2VF3DFNHWV7R(QJLQH queue. 26&B3DFNHW instances
are later freed, using )UHH26&3DFNHW (a )LIR)UHH)XQF) by way of 0VJ)LIR)UHH ,
via a mechanism which is described in more detail later.
2. The Synthesis Engine, or Engine for short (also sometimes referred to here
as the real-time context), is usually a callback function implemented by a concrete
$XGLR'ULYHU which is periodically called by the OS audio service to process and generate audio. The main steps relevant here are that the Engine calls 3HUIRUP on the
P2VF3DFNHWV7R(QJLQH and P7R(QJLQH queues, which execute the P3HUIRUP)XQF of any
messages enqueued from other threads. Messages in P2VF3DFNHWV7R(QJLQH carry
26&B3DFNHW instances which are interpreted to manipulate the Node tree, instantiate

729

26

Inside scsynths

Figure 26.2
Real-time threading and messaging implementation structure.

730

Ross Bencina

Figure 26.3
Real-time thread and queue instances and asynchronous message channels.

731

26

Inside scsynths

new Graphs, and so on. Whenever the Engine wants to perform a non-real-time safe
operation, it encodes the operation in a )LIR0HVVDJH instance and posts it to the nonreal-time thread for execution via the P)URP(QJLQH queue. Results of such operations
(if any) will be returned via the P7R(QJLQH queue. After processing messages from
P2VF3DFNHWV7R(QJLQH, P7R(QJLQH, and any previously scheduled OSC messages in
P6FKHGXOHU, the Engine performs its audio duties by arranging for real-time audio
data to be copied between OS buffers and P:RUOG!P$XGLR%XV and evaluating the
1RGH tree via P:RUOG!P7RS*URXS. When the Engine has completed lling the OS
audio output buffers, it calls 6LJQDO on P$XGLR6\QF and returns to the OS.
3. Before the server starts servicing OS audio requests, it creates a thread for executing real-time unsafe operations (the non-real-time or NRT thread). This thread waits
on P$XGLR6\QF until it is signaled by the Engine. When the non-real-time thread
wakes up, it calls )UHH and 3HUIRUP on the P)URP(QJLQH queue to perform any
non-real-time safe operations which the server has posted, then processes the
P7ULJJHUV, P1RGH(QGV, and P'HOHWH*UDSK'HIV queues. These queues contain notications of server events. Performing the enqueued notication messages results in OSC
messages being sent to clients referenced by 5HSO\$GGUHVV. After calling 3HUIRUP
on all queues, the non-real-time thread returns to waiting on P$XGLR6\QF until it is
next wakened by the Engine. Note that P$XGLR6\QF is used to ensure that the NRT
thread will always wake up and process Engine requests in a timely manner. However, it may never sleep, or it may not process the queues on every Engine cycle if
it is occupied with time-consuming operations. This is acceptable since the Engine
assumes non-real-time operations will take as long as necessary.
The description above has painted the broad strokes of the servers real-time behavior. Zooming in to a ner level of detail reveals many interesting mechanisms
which are worth the effort to explore. A number of these are discussed in the sections which follow.
26.3.2.1 Real-time memory pool allocator
Memory allocations performed in the real-time context, such as allocating memory
for new *UDSK instances, are made using the $OORF3RRO class. $OORF3RRO is a reimplementation of Doug Leas fast general-purpose memory allocator algorithm (Lea,
2000). The implementation allocates memory to clients from a large, preallocated
chunk of system memory. Because $OORF3RRO is invoked only by code running in the
real-time context, it doesnt need to use locks or other mechansims to protect its
state from concurrent access and hence is real-time safe. This makes it possible for
the server to perform many dynamic operations in the real-time thread without
needing to defer to an NRT thread to allocate memory. That said, large allocations
and other memory operations which are not time-critical are performed outside the
real-time context. Memory allocated with an $OORF3RRO must of course also be freed

732

Ross Bencina

into the same $OORF3RRO, and in the same execution context, which requires some
care to be taken. For example, )LIR0VJ instances posted by the Engine to the NRT
thread with a payload allocated by $OORF3RRO must ensure that the payload is always
freed into $OORF3RRO in the real-time execution context. This can be achieved using
0VJ)LIR)UHH , which is described in the next section.
25.3.2.2 FIFO queue message passing
As already mentioned, scsynth uses FIFO queues for communicating between
threads. The basic concept of a FIFO queue is that you push items on one end of the
queue and pop them off the other end later, possibly in a different thread. A xedsize queue can be implemented as a circular buffer (also known as a ring buffer) with
a read pointer and a write pointer: new data are placed in the queue at the write
pointer, which is then advanced; when the reader detects that the queue is not empty,
data are read at the read pointer and the read pointer is advanced. If theres guaranteed to be only 1 reading thread and 1 writing thread, and youre careful about how
the pointers are updated (and take care of atomicity and memory ordering issues)
then its possible to implement a thread-safe FIFO queue without needing to use any
locks. This lock-free property makes the FIFO queue ideal for implementing realtime interthread communications in scsynth.
The queues which we are most concerned with here carry a payload of message
objects between threads. This is an instance of the relatively well known Command
design pattern (Gamma et al., 1995). The basic idea is to encode an operation to be
performed as a class or struct, and then pass it off to some other part of the system
for execution. In our case the Command is a struct containing data and a pair of
function pointers, one for performing the operation and another for cleaning up. We
will see later that scsynth also uses a variant of this scheme in which the Command
is a C++ class with virtual functions for performing an operation in multiple stages.
But for now, lets consider the basic mechanism, which involves posting )LIR0VJ instances to a queue of type 0VJ)LIR.
Figure 26.2 shows that P2VF3DFNHWV7R(QJLQH, P7R(QJLQH, and P)URP(QJLQH queues
carry )LIR0VJ objects. The code below shows the )LIR0VJ)XQF type and the key elds
of )LIR0VJ.
W\SHGHIYRLG )LIR0VJ)XQF VWUXFW)LIR0VJ 
VWUXFW)LIR0VJ^

)LIR0VJ)XQFP3HUIRUP)XQF
)LIR0VJ)XQFP)UHH)XQF
YRLG P'DWD

`

733

26

Inside scsynths

To enqueue a message, the sender initializes a )LIR0VJ instance and passes it to


0VJ)LIR:ULWH . Each FifoMsg contains the function pointer members P3HUIRUP
)XQF and P)UHH)XQF. When the receiver calls 0VJ)LIR3HUIRUP , the P3HUIRUP)XQF
of each enqueued message is called with a pointer to the message as a parameter.
0VJ)LIR also maintains an additional internal pointer which keeps track of which
messages have been performed by the receiver. When 0VJ)LIR)UHH is called by
the sending execution context, the P)UHH)XQF is invoked on each message whose
P3HUIRUP)XQF has already completed. In a moment we will see how this mechanism
is used to free SequencedCommand objects allocated in the real-time context.
A separate 0VJ)LIR1R)UHH class is provided for those FIFOs which dont require
this freeing mechanism, such as P7ULJJHUV, P1RGH(QGV, and P'HOHWH*UDSK'HIV. These
queues carry specialized notication messages. The functionality of these queues
could have been implemented by dynamically allocating payload data and sending it
using )LIR0VJ instances; however, since 0VJ)LIR and 0VJ)LIR1R)UHH are templates
parameterized by message type, it was probably considered more efcient to create
separate specialized queues using message types large enough to hold all of the necessary data rather than invoking the allocator for each request.
The )LIR0VJ mechanism is used extensively in scsynth, not only for transporting
OSC message packets to the real-time engine but also for arranging for the execution
of real-time unsafe operations in the NRT thread. Many server operations are implemented by the FifoMsgFuncs dened in 6&B0LVF&PGVFSS. However, a number of
operations need to perform a sequence of steps alternating between the real-time
context and the NRT thread. For this, the basic )LIR0VJ mechanism is extended
using the 6HTXHQFHG&RPPDQG class.
26.3.2.3 SequencedCommand
Unlike )LIR0VJ, which just stores two C function pointers, 6HTXHQFHG&RPPDQG is a
C++ abstract base class with virtual functions for executing up to 4 stages of a process. Stage 1 and 3 execute in the real-time context, while stages 2 and 4 execute in
the NRT context. The 'HOHWH function is always called in the RT context, potentially providing a fth stage of execution. SequencedCommands are used for operations which need to perform some of their processing in the NRT context. At the
time of writing, all 6HTXHQFHG&RPPDQG subclasses were dened in 6&B6HTXHQFHG&RPPDQG
FSS. They are mostly concerned with the manipulation of SndBufs and GraphDefs.
(See table 26.1 for a list of SequencedCommands dened at the time of writing.)
To provide a concrete example of the SequencedCommand mechanism, we turn to
the Help le for Buffer (aka 6QG%XI), which reads: Buffers are stored in a single
global array indexed by integers beginning with zero. Buffers may be safely allocated, loaded and freed while synthesis is running, even while unit generators are
using them. Given that a SndBufs sample storage can be quite large, or contain

734

Ross Bencina

Table 26.1
Subclasses of 6HTXHQFHG&RPPDQG Dened in SC_SequencedCommand.cpp
Buffer Commands

BufGenCmd, BufAllocCmd, BufFreeCmd, BufCloseCmd,


BufZeroCmd, BufAllocReadCmd, BufReadCmd,
SC_BufReadCommand, BufWriteCmd

GraphDef Commands

LoadSynthDefCmd, RecvSynthDefCmd, LoadSynthDefDirCmd

Miscellaneous

AudioQuitCmd, AudioStatusCmd, SyncCmd, NotifyCmd,


SendFailureCmd, SendReplyCmd, AsyncPlugInCmd

sample data read from disk, it is clear that it needs to be allocated and initialized in
the NRT thread. We now describe how the SequencedCommand mechanism is used
to implement this behavior.
To begin, it is important to note that the 6QG%XI class is a relatively lightweight
data structure which mainly contains metadata such as the sample rate, channel
count, and number of frames of the stored audio data. The actual sample data are
stored in a dynamically allocated oating-point array pointed to by 6QG%XIGDWD. In
the explanation which follows, we draw a distinction between instance data of
6QG%XI and the sample data array pointed to by 6QG%XIGDWD.
In contrast to the client-oriented worldview presented in the Help le, :RUOG actually maintains 2 separate arrays of 6QG%XI instances: P6QG%XIV and P6QG%XIV1RQ
5HDO7LPH0LUURU. Each is always in a consistent state but is accessed or modied only
in its own context: P6QG%XIV in the RT context via :RUOGB*HW%XI and P6QG%XIV1RQ
5HDO7LPH0LUURU in the NRT thread via :RUOGB*HW157%XI . On each iteration the
engine performs messages in P7R(QJLQH and then evaluates the 1RGH tree to generate
sound. Any changes to P6QG%XIV made when calling P7R(QJLQH!3HUIRUP are
picked up by dependent Units when their UnitCalcFunc is called.
The code may reallocate an existing SndBufs sample data array. It is important
that the old sample data array is not freed until we can be certain no 8QLW is using it.
This is achieved by deferring freeing the old sample data array until after the new
one is installed into the RT contexts P6QG%XIV array. This process is summarized in
gure 26.4. The details of the individual steps are described below.
We now consider the steps performed at each stage of the execution of%XI$OORF
5HDG&PG, a subclass of 6HTXHQFHG&RPPDQG, beginning with the arrival of an 26&B3DFNHW
in the real-time context. These stages are depicted in 4 sequence diagrams, gures
26.5 through 26.8. The exact function parameters have been simplied from those
in the source code, and only the main code paths are indicated to aid understanding.
The OSC message to request allocation of a Buffer lled with data from a sound le
is as follows:
EBDOORF5HDGEXIQXPSDWKVWDUW)UDPHQXP)UDPHV

735

26

Inside scsynths

Figure 26.4
Overview of multithreaded processing of the EBDOORF5HDG command.

Stage 1 (see gure 26.5): The real-time context processes an OSC packet containing the EBDOORF5HDG message. The OSC dispatch mechanism looks up the correct
function pointer to invoke from J&PG/LEUDU\, in this case PHWKBEBDOORF5HDG .
PHWKBEBDOORF5HDG calls &DOO6HTXHQFHG&RPPDQG to instantiate a new %XI$OORF
5HDG&PG instance (a subclass of 6HTXHQFHG&RPPDQG) which we will call FPG. &DOO
6HTXHQFHG&RPPDQG calls FPG!,QLW , which unpacks the parameters from the OSC
packet and then calls FPG!&DOO1H[W6WDJH , which in turn invokes FPG!6WDJH ,
which in the case of %XI$OORF5HDG&PG does nothing. It then enqueues FPG to the NRT
thread, using 6HQG0HVVDJH)URP(QJLQH with 'R6HTXHQFHG&RPPDQG as the )LIR0VJ
)XQF.
Stage 2 (see gure 26.6): Some time later, the P)URP(QJLQH FIFO is processed in
the NRT thread. The )LIR0VJ containing our FPG is processed, which results in
FPG!6WDJH being called via 'R6HTXHQFHG&RPPDQG and FPG!&DOO1H[W6WDJH .
cPG!6WDJH does most of the work: rst it calls :RUOGB*HW157%XI , which retrieves a pointer to the NRT copy of the 6QG%XI record for FPG!P%XI,QGH[. Then it
opens the sound le and seeks to the appropriate position. Assuming no errors have
occurred, the pointer to the old sample data array is saved in FPG!P)UHH'DWD so it
can be freed later. Then DOORF%XI is called to update the 6QG%XI with the new le
information and to allocate a new sample data array. The data are read from the le
into the sample data array and the le is closed. A shallow copy of the NRT SndBuf
is saved in FPG!P6QG%XI. Finally, FPG!&DOO1H[W6WDJH enqueues the FPG with the
real-time context.
Stage 3 (see gure 26.7): Similarly to stage 2, only this time in the real-time
context, FPG!6WDJH is called via 'R6HTXHQFHG&RPPDQG and FPG!&DOO1H[W6WDJH .
A pointer to the real-time copy of the 6QG%XI for index FPG!P%XI,QGH[ is retrieved

736

Ross Bencina

Figure 26.5
Stage 1 of processing the EBDOORF5HDGcommand in the real-time context.

Figure 26.6
Stage 2 of processing the EBDOORF5HDG command in the non-real-time (NRT) context.

737

26

Inside scsynths

Figure 26.7
Stage 3 of processing the EBDOORF5HDG command in the real-time context.

using :RUOGB*HW%XI FPG!P%XI,QGH[ , and the 6QG%XI instance data initialized in


stage 2 is shallow copied into it from FPG!P6QG%XI. At this stage the sample data
array which was allocated and loaded in stage 2 is now available to Units calling
:RUOGB*HW%XI . FPG is then sent back to the non-real-time thread.
Stage 4 (see gure 26.8): Once again, back in the non-real-time thread,
FPG!6WDJH is invoked, which frees the old sample data array which was stored
into FPG!P)UHH'DWD in stage 2. Then the 6HQG'RQH routine is invoked, which sends
an OSC notication message back to the client who initiated the Buffer allocation. Finally, FPG is enqueued back to the real-time context with the )UHH6HTXHQFHG&RPPDQG
)LIR0VJ)XQF, which will cause FPG to be freed, returning its memory to the real-time
$OORF3RRO.
26.3.2.4 Processing and dispatching OSC messages
The 3URFHVV26&3DFNHW function provides a mechanism for injecting OSC messages into the real-time context for execution. It makes use of P'ULYHU/RFN to ensure
that only 1 thread is writing to the P2VF3DFNHWV7R(QJLQH queue at any time (this
could occur, for example, when multiple socket listeners are active). To inject an
OSC packet using 3URFHVV26&3DFNHW , the caller allocates a memory block using
PDOORF , lls it with an OSC packet (for example, by reading from a network
socket), and then calls 3URFHVV26&3DFNHW . 3URFHVV26&3DFNHW takes care of enqueuing the packet to the P2VF3DFNHWV7R(QJLQH queue and deleting packets, using
IUHH , once they are no longer needed.

738

Ross Bencina

Figure 26.8
Stage 4 of processing the EBDOORF5HDG command in the non-real-time (NRT) context.

Once the real-time context processes OSC packets, they are usually freed using the
0VJ)LIR message-freeing mechanism; however, packets whose time-stamp values are
in the future are stored in the P6FKHGXOHU 3ULRULW\4XHXH for later execution. Once a
scheduled packet has been processed, it is sent to the NRT thread to be freed.
scsynth dispatches OSC commands by looking up the 6&B&RPPDQG)XQF associated
with a given OSC address pattern. At startup 6&B0LVF&PGVFSS wraps these functions
in /LE&PG objects and stores them into both the J&PG/LE hash table and J&PG$UUD\
array.
OSC commands sent to the server may be strings or special OSC messages with a
4-byte address pattern in which the low byte is an integer message index. Command
strings are compatible with any OSC client, whereas the integer command indices
are more efcient but dont strictly conform to the OSC specication. When integer
command indices are received, 3HUIRUP26&0HVVDJH looks up the appropriate
6&B&RPPDQG)XQF in the J&PG$UUD\ array; otherwise it consults the J&PG/LE hash table.
The P7ULJJHUV
, P1RGH(QGV
, and P'HOHWH*UDSK'HIV
FIFOs are used by the realtime context to enqueue notications which are translated into OSC messages in
the NRT thread and are sent to the appropriate reply address by invoking
5HSO\$GGUHVVP5HSO\)XQF.
26.3.2.5 Fixed-size data structures
In real-time systems a common way to avoid the potential real-time unsafe operation
of reallocating memory (which may include the cost of making the allocation and of

739

26

Inside scsynths

copying all of the data) is simply to allocate a large enough block of memory in
the rst place and have operations fail if no more space is available. This xed-size
allocation strategy is adopted in a number of places in scsynth, including the size of

FIFO queues which interconnect different threads


P$OORF3RRO (the real-time contexts memory allocator)
The P6FKHGXOHU priority queue for scheduling OSC packets into the future
The P1RGH/LE hash table, which is used to map integer 1RGH IDs to 1RGH pointers.

In the case of P1RGH/LE the size of the table determines the maximum number of
the server can accommodate and the speed of 1RGH lookup as P1RGH/LE becomes full. The sizes of many of these xed-size data structures are congurable in
:RUOG2SWLRQV (in general, by command line parameters), the idea being that the default values are usually sufcient, but if your usage of scsynth causes any of the default limits to be exceeded, you can relaunch the server with larger sizes as necessary.
1RGHV

26.4

Low-Level Mechanisms
As may already be apparent, scsynth gains much of its power from efcient implementation mechanisms. Some of these fall into the category of low-bounded complexity methods which contribute to the real-time capabilities of the server, while
others are more like clever optimizations which help the server to run faster. Of
course the whole server is implemented efciently, so looking at the source code will
reveal many more optimizations than can be discussed here; however, a number of
those which I have found interesting are briey noted below. As always, consult the
source code for more details.
The Str4 string data type consists of a string of 32-bit integers, each containing 4
chars. Aside from being the same format that OSC uses, the implementation improves the efciency of comparison and other string operations by being able to
process 4 chars at once.
Hash tables in scsynth are implemented using open addressing with linear probing
for collision resolution. Although these tables dont guarantee constant time performance in the worst case, when combined with a good hashing function (Wang,
2007) they typically provide close to constant performance so long as they dont get
too full.
One optimization to hashing used in a number of places in the source code is that
the hash value for each item (such as a 1RGH) is cached in the item. This improves
performance when resolving collisions during item lookup.
The :RUOG uses a touched mechansim which Units and the AudioDriver can use
to determine whether audio or control buses have been lled during a control cycle:

740

Ross Bencina

maintains the P%XI&RXQWHU, which is incremented at each control cycle. When


a Unit writes to a bus, it sets the corresponding touched eld (for example, in the
P$XGLR%XV7RXFKHG array for audio buses) to P%XI&RXQWHU. Readers can then check the
touched eld to determine whether the bus contains data from the current control
cycle. If not, the data doesnt need to be copied and zeros can be used instead.
Delay lines typically output zeros until the delay time reaches the rst input sample. One way to handle this is to zero the internal delay storage when the delay is
created or reset. The delay unit generators in scsynth (see 'HOD\8*HQVFSS) avoid this
time-consuming (and hence real-time unsafe) operation by using a separate UnitCalcFunc during the startup phase. For example, %XI'HOD\1BQH[WB] outputs zeros for
the rst EXI6DPSOHV samples, at which point the UnitCalcFunc is switched to
%XI'HOD\1BQH[W , which outputs the usual delayed samples.
For rate-polymorphic units, the dynamic nature of UnitCalcFuncs is used to select
functions specialized to the rate type of the Units parameters. For example,
%LQDU\2S8JHQVFSS denes UnitCalcFuncs which implement all binary operations in
separate versions for each rate type. For example, there are separate functions
for adding an audio vector to a constant, DGGBDL , and adding 2 audio vectors,
DGGBDD . When the binary-op 8QLW constructor %LQDU\2S8*HQB&WRU is called, it
calls &KRRVH1RUPDO)XQF to select among the available UnitCalcFuncs based on the
rate of its inputs.
:RUOG

This concludes our little journey through the wonderful gem that is scsynth. I invite you to explore the source code yourself; it has much to offer, and its free!
References
Gamma, E., R. Helm, R. Johnson, and J. Vlissides. 1995. Design Patterns: Elements of Reusable Design. Reading, MA: Addison-Wesley.
Lea, D. 2000. A Memory Allocator, <http://g.oswego.edu/dl/html/malloc.html> (accessed
January 9, 2008).
McCartney, J. 2002. Rethinking the Computer Music Language: SuperCollider. Computer
Music Journal, 26(4): 6168.
Wang, T. 1997. Integer Hash Function, <http://www.concentric.net/~Ttwang/tech/inthash
.htm> (accessed January 9, 2008).

Subject Index

This index includes topics from the main body of the text. Ubiquitous topics have been limited
to principal references. For messages and classes from the SC language, see the code index.
For denitions of terms, see the syntax appendix.
12-Tone Matrix, 3435
Abstraction, 210211. See also chapter 7
Additive Synthesis, 3, 6, 3437, 128
AIFF, 25, 195, 254, 483485
Algorithm (algorithmic), 122, 420, 460, 560
composition, 599
inside a method, 161162
as pattern, 607608
for pitch extraction, 441442
synthesis, 385, 390, 644, 653
Ambient Lights (project), 118
Ambisonics, 424425
Analysis
FFT, 431
real time, 440446
signal, 61, 65
UGens, 122
Arduino, 120124
Arguments, 610, 132133, 148149
Array, 1114, 23, 2836, 5657
indexing, 238240
literal, 742744
nested, 89
ASCII, 115, 121, 128, 165, 362
Association, 163, 744
Audio rate, 17, 42, 56, 196
Balancing enclosures, 12
Beat Tracking. See Machine listening

Binary, 65, 133134


numbers, 640, 642
operators, 12
Binaural, 420422, 560, 582586. See also
chapter 19
Bipolar, 21, 4246, 57, 65
Boolean, 3133, 65
BPF (Band pass lter). See Filter
Buffer, 2429, 61, 76, 151, 184, 200, 205,
367372, 480484, 710, 725, 731734,
737
Bus, 2536, 43, 5760, 80, 538, 550
Byte Code, 146147, 676679
C++, 55, 120, 128, 178, 240, 357, 483, 578,
659, 697704, 712723, 741742
Carrier (phase modulation), 1516, 20,
45
Cents, 508509
Char, 287, 742
Class (classes), 56, 128130, 168172
as object models, 241243
tree, 173
writing, 694695
Clock (class), 83, 87, 101, 219, 228
AppClock, 83, 234, 246, 282, 626
SystemClock, 67, 8384, 549, 626
TempoClock, 8384, 197202, 645
Cloud (CloudGenerator), 258, 261, 478
480

746

Subject Index

Cocoa, 349, 356, 375


CocoaDocument (see Document)
Coding
conventions, 659660
networked live, 230
scsynth style, 722
Collection, 12, 14, 2829, 60, 87, 115, 118,
128, 132, 134, 152, 162165
Comb (N, L, C), 61, 76, 80
Combinatorics, 230
Comments, 1011, 52, 719
Compilation (compiler), 146, 659660,
664666
Composition. See chapter 3
DAW style, 53, 81, 93
object oriented (see chapter 18)
Compression, 62
Conductor. See Patterns, conductor
Constraints. See chapter 23
Control rate, 17, 26, 56, 7980, 196
ControlSpec, 277, 279
Convolution, 417
CPU (usage), 7276, 264, 401, 718719
Crucial library. See Libraries, crucial
Csound, 61, 723
Cue Players, 91
DAW (Digital audio workstation)
Composition (see Composition, DAW)
DC (offset), 716717
Debugging, 48, 55, 62, 108, 325, 361, 717
Decorrelation, 428436
Delay, 6162, 76, 7980, 415417
Devices, external. See External devices
Dialects, 635637. See also chapter 23
Dialogue (windows), 99, 151, 302
Dictionary, 139, 141, 163, 165
Distortion, 62, 494
Document
Emacs, 373
OS X GUI (Cocoa), 299303
Dot (receiver dot message), 1011, 114, 130
Drag and Drop, 288
Emacs (scel), 355357, 366374
Encapsulation, 557564

Enclosures, 12
Envelope, 18, 25, 47, 99, 343, 345, 414
Environment, 55, 120124, 166167, 648
variables, 25, 41, 139
Evaluation (of code), 4, 7, 146
Event, 180182. See also Patterns; chapter 6
note (keys), 184189
as object models, 241243
PatternProxy, 220221
protoEvent, 193197, 202205, 603605
streams, 220222, 225230
triggering, 55
Extensions. See Libraries
External devices. See chapter 4
FFT, 440442
Filter, 5, 17, 61, 68, 80
BPF, 122, 212
HPF, 80, 122
Klank, 36, 38, 50
Lag (Lag2), 62, 69, 185, 212
LeakDC, 402
LPF, 80, 397, 429, 692693
Median, 122
Ringz, 231232, 346, 353
RLPF, 3, 345
Flange, 695, 698, 703
Float (oating-point), 11, 239
Flow control, 160162
FlowLayout, 285
Fourier, 357, 417
Frequency modulation (FM). See
Modulation, frequency
FreeVerb, 6870, 417, 419
Function, 1114, 60, 143144, 147
FunctionDef, 671
iterating, 152
return, 130131, 144
Garbage collection, 659660, 684685
Gate, 18, 28, 151
Gestures, 97
Granular synthesis, 64, 80, 197, 258, 432,
465469. See also Microsound
client-side, 432433
server side, 480483

747

Subject Index

sound les, 489490


wave sets, 490500
Grouping. See Precedence
GUI (Graphical user interface). See also
Platforms; chapters 912
cross-platform, 298299
dynamically generated, 295297
Emacs (see chapter 12)
JSCUserView, 319321
OS X, 274276
SCUser, 291294
static (singleton), 298
SwingOSC (see chapter 10)
tuning, 529
windows, 349
GVerb, 6870, 417, 419
Harmonic spectrum. See Spectrum,
harmonic series
HID (Human Interface Devices), 105111
Linux, 365
HierSch, 644647
History, 230235, 243
HPF (High pass lter). See Filter
Human Interface Devices. See HID
IdentityDictionary, 163, 165166, 183
If (statements). See Flow control
Inharmonic spectrum. See Spectrum,
inharmonic
Inheritance, 129, 168171
Instance methods, 130, 137, 139
Instance variables. See Variables, instance
Interpolation, 56, 65, 68, 74, 76, 7980
Interpreter, 180, 182, 205, 208209, 240,
246, 307, 679685
variables, 140141
Introspection. See Linux, introspection
iPhone, 633
Iteration, 2831, 152153
ixiQuarks, 614619, 624628
JACK. See Linux, JACK
Japan. See chapter 22
Java, 128309, 315319, 326329
JITLib (Just In Time), 102, 480, 603, 648

JSCUserView. See GUI


Juggling, 395
Key Tracking. See Machine listening
Keyboard and Mouse, 286287
Keywords, 16, 132, 171, 741
Klank. See Filter
Lag (Lag2), 62, 69, 185, 212
LazyEnvir, 211, 215, 646
LeakDC. See Filter
Libraries
C, 659
chucklib, 603607
crucial, 303
dewdrop_lib, 589611
extensions, 55, 62, 79, 303, 546, 572
Linux, 359360
platform specic, 719720
Windows (platform), 351
quarks, 615
Linear. See Interpolation
Linux, 34, 11. See also chapter 12
ALSA, 363365
introspection, 371372
JACK, 362363
Live performance. See chapter 20
ListPattern, 141
Literals, 129130
Localization, 385
Logical expressions, 33. See also ==, !=, >, <,
>=, <=, and, or in code index
Loop, 2530
innite, 153, 218
LPF (Low pass lter). See Filter
Mac OS X. See Platforms
Machine listening. See chapter 15
beat tracking, 450452
key tracking, 452
onset detection, 446450
transcription, 452453
Map (Mapping), 58, 65, 106, 108,
122124, 385386, 401405
Markov, 41
Matrix, 12-Tone. See 12-Tone matrix

748

Subject Index

Max/MSP, 14, 156, 308, 375, 457, 660


Median. See Filter
Message (method), 104, 130131
chains, 11, 130
instance, 130, 137, 139
nested, 812
Method. See Message
MetaClass, 172
Microsound. See Granular synthesis; chapter
16
MIDI, 3033, 105, 111114, 589, 592593,
595, 608, 611
MIDIIn, 594595
MIDIResponder, 595
Modulation, 79
frequency, 15, 43, 385, 643
index, 16
phase, 3, 15, 2021, 29, 47
pulse-width, 120
Modulo (Mod, %), 18, 31
Mono (Monophonic), 14, 58, 411412
Mouse. See Keyboard and Mouse
Multichannel (expansion), 14, 17, 5758,
205, 410413
Nesting, 812
Networked live coding. See Coding,
networked live
Nil, 662, 672705
Node, 24, 28, 5961, 184185, 210217,
221222, 305, 316, 368
NodeProxy, 211, 228, 230, 404
NodeProxyEditor, 228
Noise, 62, 68, 74, 79, 431
Nyquist, 442, 716717
Object(s), 128
layout, 660664
modeling (see chapter 8)
oriented composition (see chapter 18)
oriented programming, 127129
Offset, 4248
Onset detection. See Machine listening
Open Sound Control. See OSC
Operators, 12, 31, 133134, 164
Optimization, 61, 72, 74, 718
OS X. See Platforms

OSC (Open Sound Control), 65, 89, 105,


114, 538, 737
Panning, 57, 62, 72, 385386, 411414
Parallel FX, 25, 28
Patterns, 189195. See also Events; chapter
6
conductor, 200201
empty, 219
proxy, 215, 220222, 226
recursive, 222
Phase, 56, 79, 128, 228
modulation (see Modulation, phase)
spectrum decorrelation, 431, 435
Physical Model, 644
Pink Noise, 343
Platforms
Linux (see chapter 12)
Max OS X (see chapter 9)
Windows (see chapter 11)
Plug-ins. See chapter 25
Polymorphism, 87, 89, 104, 168, 237238,
564, 637
Precedence, 12, 37, 134
Precedence effect, 411, 437
Programming. See chapter 5
Primitives, 684689
Prototypes, 598602
ProxySpace, 345
PySCLang, 352353
QCD (quantum chromodynamics), 252258
Quantization, 197, 199, 228
Quarks, 105, 359. See also ixiQuarks
Random
button, 265266
collection (array), 132, 162
number generators, 6164, 668, 710
patterns, 190
parameter choices, 8, 1314, 3035,
8788, 90
pseudo, 80, 710
range, 3132, 38, 261262 (see also rrand
in code index)
seed, 8, 69
server side 6869, 189190

749

Subject Index

Ratios. See Tuning


Rate
audio (see Audio rate)
control (see Control rate)
sample (see Sample rate)
Recursion (recursive), 153155, 225227,
614, 665
Receiver, 10, 130
Recording, 53, 93, 9598
References, 142
Reverb, 409, 417420
Ringz. See Filter
RLPF (Resonant low pass lter). See Filter
Routine, 8384, 87, 98, 102, 603604, 607
Sample and Hold, 47, 49, 79
Sample rate, 196, 445, 641, 698, 708709,
734
Scale (collection of notes), 28, 31, 34, 41,
128, 167
microtonal (see chapter 17)
Scale (relative size), 4348, 386, 608610
time, 489490
scel. See Emacs
Scheduler, 638, 645
Scheduling, 8387
constraints, 635, 644647
sclang, 360362
Scope, variable. See Variables, scope
Score, 8791, 371. See also chapter 18
scsynth, 5556, 60, 65, 72. See also chapter
26
freeing, 197
SCView, 291, 301, 303
SCWindow, 274
Sequences (Sequencer), 3841
SerialPort, 117, 119, 121
Server, 5569, 106, 109, 115117, 305307,
315320, 326328
node, 211214
options, 24, 59, 74
synthesis, 6, 10, 2224, 5253
window, 4, 57, 95
Windows (platform), 349
Shaper, 62
SharedIn, 349
Shout Window, 243255

Sidebands, 20, 46
Slider, 274276, 280, 307308, 311314
Smalltalk, 207, 237240, 269, 375, 570
Sonication, 236, 252258. See also chapter
13
Spatialization. See chapter 14
3D audio, 420421
Spectrum, 34, 61, 68, 80, 403, 405
diffusion, 432435
harmonic series, 1516, 28, 3335, 41,
4547, 441, 528
inharmonic, 34
StartUp, 306307, 340341, 347352,
366
Streams, 199202. See also Patterns
String, 11, 14, 23, 51, 361
Subtractive Synthesis, 3, 5
Switch (statements). See Flow control
SwingOSC. See GUI
Symbol, 12, 129
streams, 222
Synth Denitions, 2124, 179180. See also
chapter 6
Synthesis, non-real-time. See chapter 18
Task, 2932, 3941, 83, 8590
TaskProxy, 215218, 262265
Tempo clocks. See Clock, TempoClocks
Transcription. See Machine listening
Tuning. See chapter 17
equal, 505509
just, 515516
odd Limit, 517
poly, 519521
ratios, 515
tonality Diamond, 518
unequal divisions, 515
UGen (Unit Generator), 10. See also chapter
2 and chapter 25
pseudo, 691
UI. See GUI
Unicode, 287
Unipolar, 4446, 65
Unit Generator. See UGen
UNIX, 115116, 330, 360361
USB, 106, 120121

750

Subject Index

Variables, 1823, 31, 135141


class, 139
environment, 139
instance, 137140, 156158, 166, 169,
202
interpreter, 140
pseudo, 140
scope, 25, 155156
versus references, 142
VBAP (Vector based amplitude panning),
323324
VEP (Virtual electronic poem), 575587
View. See GUI
Voicer, 592594
Voltage Control (VCO, VCF, VCA), 17
Wacom, 106, 109, 111, 124
Wave Field Synthesis, 425427
Wave sets. See Granular synthesis
Wavetable, 163164
Wii, 106, 109110, 357
Window
as GUI, 274275
shout (see Shout window)
Windows (platform), 34, 11. See also
chapter 12

Code Index

This index contains language elements of SuperCollider. While most terms are used throughout the text, this index is limited to initial references, typically from the tutorial chapters.
Note that this index is divided into two sections: messages and classes.
background, 107, 244247
bufnum, 26, 418

Messages
 , 33, 92, 313
, 522, 709
 PRGPRGXOXV ,

3133

,

42, 63, 94
, 94, 135
 , 33
!, 293, 564
, 3133
! , 33, 171, 174
abs, 66, 398
add, 22, 24, 39, 5859, 6364
addAll, 134, 150
adsr, 345
amp, 312, 486, 494
ampdb, 313, 455
and, 3233
ar, 416, 5859
asArray, 120, 204, 256, 529530
asAscii, 120
asCompileString, 302
asInteger, 455
asKeyValuePairs, 295
asr, 531532
asStream, 89, 101, 201, 220221, 488,
500503
asString, 12, 42, 119, 134, 142
at (accessing elements of a collection),
2930, 113, 132, 203
audio, 30, 73

choose, 13, 32, 3840, 100


class, 141, 172, 330
clear, 210, 215216, 221, 244247, 250
clip, 324, 402, 637, 641
clock, 204
close, 59, 63, 86, 120, 244245, 251,
255
coin, 3233, 498, 502
collect, 9194, 120, 625
connect, 111, 116, 364, 365
control, 2728, 30, 296
copy, 193, 262263, 266267
copyRange, 494
count, 159, 176
cpsmidi, 11, 66, 191
current, 255, 260267
curve, 321
dbamp, 69, 70, 7576
decorator, 86, 92, 98
def, 147, 154, 296
default
Server, 4, 133, 185
SwingOSC, 306307
TempoClock, 197, 647
defer, 83, 176, 283, 296, 297
degreeToKey, 513
delta, 219, 606, 651

752

Code Index

destroy, 328329, 335


device, 118, 119, 347
disconnect, 117, 349
discretize, 481488
dist, 321, 324, 430
do, 30, 64, 6976
doOnce, 67, 277
drop, 247, 249, 387
dump, 140, 241, 346
dup, 11, 13, 77, 78
duration, 553, 605, 609

info, 108
init, 113, 364
insert, 35, 51
interpret, 134, 518, 652
ir, 69, 259, 388, 391
isClosed, 245, 251
isEmpty, 362
isFloat, 33
isInteger, 32, 33
isKindOf, 161, 295, 567
items, 268, 289, 290

embedInStream, 193194
env, 486, 551
envir, 261, 266
error, 257, 329
even, 33
explin, 277, 479, 640
exprange, 64, 216, 231232

key, 267, 552, 554, 651


kr, 46, 10, 1417

fadeTime, 216, 233, 487


ll
Array, 33, 37, 50, 63, 70
Mix, 6, 97, 616617, 625
ndBy, 107108
font, 145, 244247
for, 152
forBy, 152
fork, 85, 138, 145
format, 313, 335
free, 19, 23, 5859, 63
freq, 476, 551, 555556
front, 83, 107
gap, 448, 501, 625
get, 328, 332, 387, 388, 455456
getDate, 600
getn, 447, 448
getPaths, 99, 302
global, 295, 296
globalKeyDownAction, 302
gui, 317, 595, 596
id, 326, 328, 332, 333
if, 3133, 3839, 92, 257
includes, 32
index, 644

lag, 397, 399, 402


latency, 204, 260, 485
lfo, 551, 559561
linen, 26, 388, 605
linexp, 191, 198, 277
linlin, 471, 515, 524
linrand, 434, 476, 488
load, 539, 544, 547, 548
loadCollection, 434
make, 203, 404, 471
map, 28, 216, 262
max, 6, 30, 3537, 159, 174176
midicps, 11, 13, 67, 69, 85
midiratio, 479, 506, 556
min, 49, 325, 455
mod (%), 18, 2425
mouseDownAction, 289, 292, 324
mouseUpAction, 324, 620
newMsg, 541548
next, 66, 88, 101
nextLine, 266267, 297
node, 312, 329
nodeID, 295
normalizeSum, 321, 531
not, 257, 292
notEmpty, 455, 458
noteOff, 113
noteOn, 111, 113
numChannels, 26, 329, 335
numFrames, 26, 335

753

Code Index

odd, 32, 33
onClose, 92, 145, 153
option, 346, 410, 415
or, 33
path, 26, 335
pause, 86, 95, 222
perc, 24, 39, 4850, 82
permute, 13
phase, 716
play, 1319
plot, 11, 13, 35, 351
poll, 42, 44, 63
pop, 210, 214215
pos, 321
post, 29, 32, 35, 3940
postln, 11
pow, 18, 132133
prClose, 333
put, 132, 203, 257, 261262
putAll, 119120, 219, 267
pyramid, 150
quant, 228, 260, 263
rand, 8, 1113
range, 28, 45, 64, 69, 70
ratio, 533
read, 25, 91, 99
reciprocal, 64, 78, 96, 145
record, 95
register, 302, 329
release, 6978, 8594
render, 183, 195
reset, 87, 101, 158159
reverse, 13
rotate, 13
round, 11, 66
rrand, 7176, 162
run, 69, 350
sampleRate, 335, 418, 433
sched, 67, 8384
schedAbs, 647
scope, 13, 58, 63
scramble, 13, 220221, 257
select, 295, 620

send, 233, 388, 454


sendBundle, 116, 204, 259260
sendCollection, 388, 481488
sendMsg, 115, 333, 349, 471
set, 23, 4849, 64, 140, 145146
setn, 350, 422, 649
setStarttime, 553
signalRange, 44, 494
silent, 221
sine, 198, 259, 433
slice, 256, 257
softclip, 38, 398, 402
source, 215
sourceCode, 154
squared, 130, 239, 398
standardizePath, 538547
start, 32, 40, 88
startTime, 553, 566
state, 86, 137, 150151, 266
stop, 32, 40, 8695
stream, 101, 512
string, 150, 176, 244247, 308
stringColor, 246, 252
sum, 59, 122, 149
sum3rand, 710
swing, 326, 330, 350, 376
tempo, 84, 88, 204, 647
trace, 224, 225
uid, 113, 365
value, 84, 108, 119
valueArray, 171
valueEnvir, 167
view, 150162, 244247
visible, 285
wait, 3033, 69, 85
while, 152
window, 59, 63, 250
wrap, 40
wrapAt, 3135, 202
wrapPut, 3940
write, 538
xrand, 142, 153

754

Code Index

Classes
Allpass (N, L, C), 29, 50, 416
AppClock, 83, 246, 334
Array, 150, 391
BeatTrack, 444, 450, 451
BiPanB2, 426
Blip, 14, 241
BPF, 212, 397
BrownNoise, 175, 216
BufAllpass (N, L, C), 416
BufChannels, 708, 710
BufComb (N, L, C), 415
BufDelay (N, L, C), 415
BufDur, 487, 490
Buffer, 95, 388, 507
BufFrames, 198, 418, 484
BufRateScale, 91, 198
BufRd, 56, 61, 496
BufSampleRate, 491, 547
Bus, 2728, 5961, 447
Char, 107, 119, 129, 663667
Clip, 324, 402, 637, 641
CmdPeriod, 67, 277, 368, 626
CocoaDocument, 299
Comb (N, L, C), 10, 7678
Compander, 63
Convolution, 419
Decay, 63, 7778, 212, 388
Delay (N, L, C), 63, 415416
DetectSilence, 39
Dialog, 99
Dictionary, 202, 744
Dust, 5, 19, 63, 70, 97
EnvGen, 24, 26, 64, 69, 133, 138
Environment, 139142, 166167, 239
Event, 179, 204, 239, 311312
EventStreamPlayer, 101, 197, 201, 371
ExpRand, 5, 8, 13, 68, 153
FFT, 431, 434
FlowLayout, 159, 266267, 350
Free, 19, 49, 58, 63

FSinOsc, 259
Function, 83, 87, 149, 155159, 171
GrayNoise, 133
GUI, 245, 247, 250, 266
Harmonics, 164
HPF, 122, 451, 696
IdentityDictionary, 743
Impulse, 6364, 7778, 231233, 350,
353
In, 2630, 73, 422, 591
IRand, 24, 6869
KeyCodeResponder, 287
KeyState, 106
KeyTrack, 444, 453
Klank, 36, 38, 50
Lag (Lag2), 69, 212
Latch, 49
LeakDC, 402
LFClipNoise, 2627, 212, 216
LFDNoise1, 140
LFDNoise3, 69
LFNoise (0, 1, 2), 56, 1314, 64, 66, 69,
70, 140
LFPulse, 28, 4345, 215
LFSaw, 15, 43, 346, 351
LFTri, 1720, 64, 145
Limiter, 6263
Line, 388
Linen, 1719, 91, 97, 180, 197
LinExp, 191, 198, 277
LinPan2, 413, 414
LinXFade2, 413
LPF, 397, 429, 451, 458
MIDIIn, 111, 364, 458
MIDIOut, 111, 364
Mix, 3538, 458, 616, 625
MoogFF, 61
MouseX (MouseY), 1516, 1921, 106,
122, 389
MultiSliderView, 447, 623, 625

755

Code Index

Ndef, 211, 215, 228233


Nil, 5, 39, 85, 111, 136
Node, 295
NodeProxy, 345
NoteOnResponder, 111, 112, 348
NRand, 68
OffsetOut, 180, 198, 217, 231, 259
OSCpathResponder, 295, 328
OSCresponder, 111, 116, 122, 251, 449,
455
OSCresponderNode, 65, 67, 116
Out, 2224, 2730, 5859, 6378
Pan2, 6, 11, 62, 180, 231232
Pan4, 413
PanAz, 62, 388, 402, 413, 423
PanB, 62, 73, 426
Patterns
Pbind, 101, 103, 162, 182183, 189198
Pbrown, 103, 189, 192
Pdef, 103
Pfunc, 190, 605, 609
Pkey, 191, 192, 596, 605
Pmono, 189
Ppar, 189, 222, 520,
Pproto, 205
Prand, 101, 162, 190, 192, 223, 227, 231,
647
Prout, 190, 607
Pseq, 39, 8789, 181183, 189194
Pseries, 227, 595, 606609
Pshuf, 223
Pspawner, 183
Pstep, 190, 192, 194
Pstutter, 190, 606
Psym, 222
Ptime, 606, 609
Ptpar, 194
Ptuple, 221
Pwhite, 189191, 223225, 231232, 345
Pxrand, 87, 89, 101, 345, 606, 609
PinkNoise, 344, 388, 402, 424
PlayBuf, 2530, 61, 91, 95, 198
PMOsc, 1516, 2124, 644
PopUpMenu, 268

PriorityQueue, 731, 738


ProxyMixer, 229, 345, 346
ProxySpace, 209, 229, 240, 396, 648
PV_BrickWall, 715
PV_Copy, 431, 434
PV_Diffuser, 431
PV_HainsworthFoote (PV_JensenAndersen),
444, 446
PV_MagMul, 434, 715
QuadN, 389
Ramp, 402
Rand, 24, 75, 217, 625
RandID, 69
RandSeed, 8, 69
RangeSlider, 267
RecordBuf, 712713
Rect, 86, 92, 98, 107, 129131
ReplaceOut, 60, 73
Resonz, 6970, 95, 97
Ringz, 231, 232, 346, 353, 649
RLPF, 5, 345
Rotate2, 413414
Routine, 69, 620
RunningSum, 122, 444, 454455
SampleRate, 388, 391
Saw, 345, 369, 702
SCButton, 275, 285, 299
SCCompositeView, 282, 285
SCPen, 291
SCSlider, 277, 281
SCTextField, 289
SCUserView, 291294
SCWindow, 83, 276285
SelectX, 397, 414
SelectXFocus, 414
SendReply, 68, 122
SendTrig, 65, 66, 67, 122, 283
Server, 4, 52, 5863, 295, 507
ServerOptions, 540, 545, 547, 549
SinOsc, 57, 30, 3537, 5659
Slider2D, 350
SoundFile, 195, 329, 335
SoundIn, 65, 122, 418, 440, 445

756

Code Index

Splay, 414
SplayAz, 414
StaticText, 176, 268, 617619
StereoConvolution2L, 417, 419, 422
SystemClock, 67, 8384, 549
TabletView, 109
Tdef, 215220
TempoClock, 8384, 88, 185
TExpRand, 591
TGrains, 25, 64, 480482
TIRand, 6869
TRand, 17, 1921
TToggle, 293

You might also like