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 Beginner’s Tutorial 3
David Cottle
2 The Unit Generator 55
Joshua Parmenter
3 Composition with SuperCollider 81
Scott Wilson and Julio d’Escriván
4 Ins and Outs: SuperCollider and External Devices 105
Stefan Kersten, Marije A. J. Baalman, and Till Bovermann

Advanced Tutorials 125

5 Programming in SuperCollider 127


Iannis Zannos
6 Events and Patterns 179
Ron Kuivila
7 Just-in-Time Programming 207
Julian Rohrhuber and Alberto de Campo
8 Object Modeling 237
Alberto de Campo, Julian Rohrhuber, and Till Bovermann
vi Contents

Platforms and GUI 271

9 Mac OSX GUI 273


Jan Trützschler von Falkenstein
10 SwingOSC 305
Hanns Holger Rutz
11 SuperCollider on Windows 339
Christopher Frauenberger
12 “Collision with the Penguin”: SuperCollider on Linux 355
Stefan Kersten and Marije A. J. Baalman

Practical Applications 379

13 Sonification and Auditory Display in SuperCollider 381


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

Projects and Perspectives 573

19 A Binaural Simulation of Varèse’s Poème Électronique 575


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

Developer Topics 657

24 The SuperCollider Language Implementation 659


Stefan Kersten
25 Writing Unit Generator Plug-ins 691
Dan Stowell
26 Inside scsynth 721
Ross Bencina

Appendix: Syntax of the SuperCollider Language 741


Iannis Zannos
Subject Index 745
Code Index 751
Foreword

James McCartney

Why use a computer programming language for composing music? Specifically, why
use SuperCollider? There are several very high-level language environments for
audio besides SuperCollider, such as Common Music, Kyma, Nyquist, and Patch-
work. These all demonstrate very interesting work in this area and are worth look-
ing into. SuperCollider, though, has the unique combination of being free, well
supported, and designed for real time. It is a language for describing sound pro-
cesses. 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 higher-
level 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 infinitely long pieces, infinite variations of
structure or surface detail, algorithmic mass production of synthesis voices, sonifica-
tion 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 open-
endedness, early on, I often felt it difficult to know how best to write the documenta-
tion. 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 docu-
mentation, porting to other operating systems, interfacing to hardware and soft-
ware, writing new unit generators, extending the class library, maintaining a Web
site, mailing lists, and a wiki, fixing bugs, and, finally, writing and editing the chap-
ters of this book. All of these efforts have resulted in more features, better documen-
tation, and a more complete, robust, and bugfree program.
x 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 syn-
thesize 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 imple-
menting 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 fig-
ured that the synthesizer industry was a lot smaller than I had imagined and that I
should rethink my career plans.
The first 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 CSound’s assembly-language-like syntax. Synth-O-Matic had a more
expression-oriented syntax for writing signal flow graphs and a graphical user inter-
face 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 wasn’t until the PowerPC came out that it became practical to do floating-point
signal processing in real time on a personal computer. At the time I had been work-
ing 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 first PowerPC-based ma-
chine 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 first 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 floors below my office 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 didn’t fail. To my surprise, it actually worked rather well.
The version 1 language was dynamically typed, with a C-like syntax, closures bor-
rowed 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 environ-
ments. 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 de-
scribing real-time interactive sound processes. I wanted to create a way to de-
scribe categories of sound processes that could be parameterized or customized. The
main idea of SuperCollider is to algorithmically compose objects to create sound-
generating processes. Unit generators, a concept invented by Max Matthews for his
Music N languages, are very much like objects and are a natural fit for a purely
object-oriented Smalltalk-like language.
In 2001 I was working on version 3 of SuperCollider, and because of the architec-
ture 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 difficult one to make. But finan-
cially it was looking like I would need a “real” job soon, anyway. I was also worried
that the period of self-employment on my résumé 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 traffic. 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. We’re delighted to present a collection of tu-


torials, essays, and projects that highlight one of the most exciting and powerful
audio environments. SuperCollider (SC to its friends) is a domain-specific program-
ming language specialized for sound but with capabilities to rival any general-
purpose 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. In-
deed, for many artists, SuperCollider is the first 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 you’re programming
until it’s too late and you’re hooked! The potential applications in real-time interac-
tion, installations, electroacoustic pieces, generative music, audiovisuals, and a host
of other possibilities make up for any other qualms. On top of that, it’s free, power-
ful, and open source, and has one of the most supportive and diverse user and devel-
oper 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. We’ll 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 Win-
dows 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 begin-
ners 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 aren’t 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 clarification where neces-
sary and have tried to avoid duplication of materials except where absolutely crucial
for clarity.
These “topics” chapters are divided into sections titled Advanced Tutorials, Plat-
forms and GUI, and Practical Applications. They begin with chapter 5, “Program-
ming 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 who’d rather approach SC from a language design and theory perspec-
tive than through the more user-friendly approach in chapter 1. Chapters on a variety
of subjects follow, including sonification, 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 Super-
Collider has been used in the real world. These chapters also provide some philo-
sophical insight into issues of language design and its implications (most specifically
in chapter 23, “Dialects, Constraints, and Systems-Within-Systems”). This sort of
intellectual pursuit has been an important part of SuperCollider’s development; SC
is a language that self-consciously aims for good design, and to allow and encourage
elegance, and even beauty, in the user’s code. Although this might seem a little ab-
stract at first, we feel that this sets SC apart from other computer music environ-
ments 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 be-
tween them. Most code in this book should run on all platforms with the same re-
sults, 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 auto-
matically. 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 chap-
ter 10). However, there are some differences in the programming editor environ-
ments (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 file 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, they’ll 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 aren’t 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), depend-
ing again on how formal we are being. Anyway, if you’re new to SuperCollider, don’t
worry about this at all; chapter 1 will start you on the righteous path, and you’ll
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 down-
load 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 trou-
ble. Of course, the reader may find 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 prom-
ises, in this fast-paced world of operating system shifts, that the code in this book
will remain eternally correct—the ongoing development and improvement of envi-
ronments such as SuperCollider are a big part of what makes them so exciting—
we’ve 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 you’re 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
you’re not getting any sound, remember to check if you’ve 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 po-
tential to feedback unless your monitoring arrangements are correct. The easiest way
to deal with such examples is to monitor via headphones.
We couldn’t 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 devel-
opers, 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.
We’re sure you’ll have fun as you explore this compendium, and we’re also sure
you’ll be inspired to some fantastic art and science as you go. Enjoy exploring the
SuperCollider world first charted by James McCartney but since expanded immea-
surably by everyone who partakes in this infinitely flexible 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 McCartney’s 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. SC’s 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 we’re quite sure
it was a team effort!
xvii Introduction

Many thanks to our students, who have served as guinea pigs for pedagogical ap-
proaches, 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, we’ve been working on this project for 2 years
to bring the final manuscript to fruition. Though Scott and Nick have met on many
occasions (and were co-organizers of the 2006 SuperCollider Symposium at the Uni-
versity 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 we’ve sent each other.
For many months, in the heat of the project, David modified 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.
3 Composition with SuperCollider

Scott Wilson and Julio d’Escriván

3.1 Introduction

The actual process of composing, and deciding how to go about it, can be one of
the most difficult things about using SuperCollider. People often find 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 ex-
ample, SC doesn’t present the user with a single “preferred” way of working. This
can be confusing, but it’s an inevitable side effect of the flexibility of SC, which al-
lows 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 one’s 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 can’t hope to be anywhere near exhaustive, as
the topic of the chapter is huge and in some senses encompasses all aspects of SC.
Thus we’ll take a pragmatic approach, exploring both some abstract ideas and con-
crete applications, and referring you to other chapters in this book where they are
relevant.
82 Scott Wilson and Julio d’Escriván

3.1.1 Coding for Flexibility

The notion of making things that are flexible and reusable is something that we’ll
keep in mind as we examine different ideas in this chapter. As an example, you might
have some code that generates a finished sound file, 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 fly in live performance, or be adapted to generate a new
version to different specifications (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. You’ll recall from chapter 1 that environ-
ment 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,
we’ll use them in this chapter for creating simple examples. Keep in mind, however,
that in the final 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 prac-
tical and aesthetic issues: Who is your piece for? Who is performing it? (Maybe you,
maybe an SC Luddite . . .) What kind of flexibility (or expressiveness!) is musically
meaningful in your context? Does pragmatism (i.e., maximum reliability) override
aesthetic or other concerns (i.e., you’re 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 specific times, or a collection of
things that can be triggered flexibly (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 3 Composition with SuperCollider

3.2.1 Clocks, Routines, and Tasks

Here’s 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 first argument to the VFKHG message is a delay in seconds, and the second is a
)XQFWLRQ 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 Func-
tion, using that value as the new delay time.
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 we’ll see some GUI code in a few of the examples in this chapter, we
won’t 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 aren’t
clear and try to focus on the topics at hand.
Another Clock subclass, 7HPSR&ORFN, provides the ability to schedule events ac-
cording to beats rather than in seconds. Unlike the clocks we’ve 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 there’s only one each of SystemClock and AppClock. By varying
84 Scott Wilson and Julio d’Escriván

a TempoClock’s tempo (in beats per second), you can change the speed. Here’s 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 par-


ticularly useful when composing metric music. (See the TempoClock Help file for
more details.)
Now let’s 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 you’d 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 prefera-
ble, for reasons we’ll explore below.
Now here’s 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 3 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 Routine’s SOD\
method, which is a shortcut for scheduling it in a clock. By yielding nil at a certain
point, the clock doesn’t reschedule, so you’ll 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 we’ll use a
7DVN, which you may remember from chapter 1. A Task works almost the same way
86 Scott Wilson and Julio d’Escriván


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 3 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 fixed scheduling and waiting for
a trigger to continue. The trigger needn’t 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 com-
plicated ways. You can nest Tasks and Routines or combine fixed 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 Pat-
terns often provide a convenient way to produce a Stream of values (or other ob-
jects), and that they can be usefully combined with the methods shown above.
Figure 3.4 demonstrates two simple Patterns: 3VHT and 3[UDQG. Pseq specifies an
ordered sequence of objects (here numbers used as durations of time between succes-
sive events) and a number of repetitions (in this case an infinite 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 Pat-
tern, 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 mes-
sages, 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 flexibility, the YDOXH message above dem-
onstrates 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 d’Escriván


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 3 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 ex-
ample above. Since S and T are evaluated each time through the loop, it’s even pos-
sible to do this while the 7DVN is playing. (See figure 3.5.) Taking advantage of
polymorphism in ways like this can provide great flexibility, 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 es-
sentially an ordered list of times and 26& commands. This takes the form of nested
Arrays. That is,

>
>WLPH>FPG@@
>WLPH>FPG@@

@

As you’ll 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 didn’t 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 file, we’ll just refer you there if you’d like to explore further. In any
case, if you find over time that you prefer to work in “messaging style” rather than
“object style,” you may find 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 d’Escriván


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 3 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 let’s turn to a more concrete example. Triggering sound files, a common tech-
nique 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 fledged 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 files, and the latter for sub-
stantial cues that you wouldn’t 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. Here’s a very simple example which
loads 2 files into RAM and plays them:
aP\%XIIHU %XIIHUUHDG VVRXQGVDZONZDY ORDGDVRXQG
aP\%XIIHUSOD\SOD\LWDQGQRWLFHLWZLOOUHOHDVHWKHQRGHDIWHU
SOD\LQJ

Buffer’s play method is really just a convenience method, though, and we’ll prob-
ably want to do something fancier, such as fade in or out. Figure 3.7 presents an
92 Scott Wilson and Julio d’Escriván


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 figure 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 files 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 3 Composition with SuperCollider

third-party extension classes that do things such as automating the required house-
keeping (e.g., Fredrik Olofsson’s 5HG'LVN,Q6DPSOHU).
The previous examples deal with mono files. For multichannel files (stereo being
the most common case) it is simplest to deal with interleaved files.2 Sometimes, how-
ever, 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 finding your own compositional voice. Again, an exhaustive discussion
of all of SuperCollider’s sound-generating capabilities would far exceed the scope of
this chapter, so we’ll 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 files for the sake of conve-
nience, but you should keep in mind that what we’re discussing could apply to more
or less any synthesis or processing technique.

3.3.1 Recording

At some point you’re probably going to want to record SC’s 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 file format.
To illustrate this, let’s make a sound by creating an effect that responds in an idio-
syncratic way to the amplitude of an input file and then record the result. You may
not find 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 figure 3.10.)
After executing this, you should have a sound file in SC’s recordings folder (see the
doc for platform-specific locations) labeled with the date and time SC began record-
ing: 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 figure 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 d’Escriván

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 files for multichannel cues.
95 3 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 d’Escriván

record as 32-bit floating-point values. This has the advantage of tremendous dy-
namic range, which means you don’t have to worry about clipping and can normal-
ize later, but it’s not compatible with all audio software.
VUHF6DPSOH)RUPDWB LQW 

More elaborate recording can be realized, of course, by using the 'LVN2XW UGen.
Server’s 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 find difficult to do is to stop thinking about exactly
what they want to do at the moment, and instead consider whether the problem
they’re dealing with has a general solution. Generalizing your code can be very pow-
erful. 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 we’ve 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. We’ve
97 3 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 flexible than if you think in terms of
exact implementations. Now it happens that the example above is hardly shorter
than the first, 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 we’re 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 flexible, it’s shorter;
and because of that, it’s much easier to understand. It’s 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 figure 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 computer’s being used today as a vir-
tual analog studio; many sequencer software GUIs attest to this way of thinking.
However, as software has become more accessible, programming may in fact be re-
placing this virtual splicing approach.
One of the main advantages of a computer language is generalization, or abstrac-
tion, 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 com-
pendium 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 electro-
acoustic music this can happen, for example, when we modulate 1 sound with the
98 Scott Wilson and Julio d’Escriván


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,” ges-
tures 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 Rou-
tines” (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 perfor-
mance of your piece, you can add elements of variability to what is normally fixed
99 3 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 file 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

Let’s 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 file we opened with our file 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
first sound stored in our Buffer list:
aEXIIHUV>@

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

Now, let’s 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 d’Escriván


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).
We’ll need this below. Let’s 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 ran-
domly from that list. For example, from the list we defined earlier we could do this:

6\QWK ?VDPSOH3OD\HU>?RXW?EXIQXPaEXIIHUVFKRRVH?UHO@ 

Let’s define a Routine that allows us to create a stuttering /rushing gesture in a


glitch style. We’ll use a new Pattern here, 3JHRP, which specifies 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 order-
ing, simultaneity, and internal behavior of your gestures.
Let’s 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 3 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. Here’s a simple example which makes
sound using what’s called the “default” SynthDef:

UDQGRPO\VHOHFWHGIUHTXHQF\GXUDWLRQVHFRQG
3ELQG ?IUHT3UDQG >@ ?GXU SOD\

It’s 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 defined 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 d’Escriván

If you have evaluated the expressions above, you will notice that you don’t 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 specified 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 accele-
rando 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 Event-
StreamPlayer 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).
Let’s designate another environment variable to hold a sequence of values that we
can plug in at will and change on the fly. 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 we’ll call ?D:


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

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

aUK\WKP 3VHT >


QLO@ 
103 3 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, any-
thing 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 argu-
ments 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 op-
tions. 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 files to our
audio editor. The latter approach is useful if we want to use a cue player for the final
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 specific 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 flexible 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

Budón, O. 2000. “Composing with Objects, Networks, and Time Scales: An Interview with
Horacio Vaggione.” Computer Music Journal, 24(3): 9–22.
Collins, N. 2010. Introduction to Computer Music. Chichester: Wiley.
104 Scott Wilson and Julio d’Escriván

Dodge, C., and T. A. Jerse. 1997. Computer Music: Synthesis, Composition, and Perfor-
mance, 2nd ed. New York: Schirmer.
Holtzman, S. R. 1981. “Using Generative Grammars for Music Composition.” Computer
Music Journal, 5(1): 51–64.
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 Direc-
tions in Computer Music Research, pp. 291–396. 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): 235–265.
Mathews, M. V. 1963. “The Digital Computer as a Musical Instrument.” Science, 142(3592):
553–557.
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 Compo-
sition. York, UK: Orpheus the Pantomime.

Notes

1. You may have noticed that the terms “message” and “method” used somewhat inter-
changeably. 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 Wilson’s De-Interleaver application for OSX and Jeremy Friesner’s cross-platform
command line tools audio_combine and audio_split allow for convenient interleaving and
deinterleaving of audio files.
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, allow-
ing 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 Pro-
gramming 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.
You’re probably already familiar with UGens from other chapters. Before creating
new UGens of your own, let’s first consider what a UGen really is, from the plug-in
programmer’s point of view.

25.1 What Is a UGen, Really?

A UGen is a component for the synthesis server, defined in a plug-in, which can re-
ceive a number of floating-point data inputs (audio- or control-rate signals or con-
stant values) and produce a number of floating-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 define synthesis graphs in SC language (Synth-
Defs). 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 file for the language client.

25.2 An Aside: Pseudo UGens

Before we create a “real” UGen, we’ll look at something simpler. A pseudo UGen is
an SC class that “behaves like” a UGen from the user’s point of view but doesn’t
692 Dan Stowell

involve any new plug-in code. Instead, it just encapsulates some useful arrangement
of existing units. Let’s create an example, a simple reverb effect:

5HYHUE^
 DU^_LQ_
YDURXW LQ
RXW $OOSDVV1DU RXWUDQG 
ARXW
`
`

This isn’t a very impressive reverb yet, but we’ll 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 file and save it as, for instance, Reverb1.sc in your
SCClassLibrary or Extensions folder; then recompile. You’ll now be able to use
5HYHUEDU within your SynthDefs, just as if it were a “real” UGen. Let’s 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 file, 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 definite limitations. It is of course confined to processes that
can be expressed as a combination of existing units — it can’t create new types of
processing or new types of server behavior. It may also be less efficient 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 let’s 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.
Efficiency should also be a consideration.
2. Second, write the Help file. Really — it’s a good idea to do this before you start
coding, even if you don’t 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 file is also a good reminder of what the UGen does — don’t underestimate
the difficulties of returning to your own code, months or years later, and trying to
decipher your original intentions!
The Help file will be an HTML file with the same name as the UGen. There is a
“Documentation Style Guide” in the SC Help system which includes tips and recom-
mendations for writing Help documentation. But, of course, during development the
Help file doesn’t need to be particularly beautiful.
3. Third, write the class file. You don’t need to do this before starting on the C++
code, but it’s a relatively simple step. Existing class files (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 file and writing the plug-in code.
694 Dan Stowell

25.4 Writing the Class File

A class file 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 defined in the 8*HQ class can be
used when the language builds the SynthDef (synth graph definition).
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 (defined in the main 8*HQ class),
which processes the arguments and adds the UGen correctly to the SynthDef that is
being built.

The class file does not have any direct connection with the C++ plug-in code — after
all, it’s the server that uses the plug-in code, while the class file is for the language
client.
Let’s 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 we’ll discuss
shortly.
The first 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 ar-
rays, 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 unit’s output. As well as avoiding the pro-
grammer’s 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 final 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 files.)
Let’s start to draft the class file for a UGen we can implement. We’ll create a basic
“flanger” 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, you’ll find that
this is sufficient to allow you to use )ODQJHUDU or )ODQJHUNU in SynthDefs, which
the SuperCollider language will happily compile — but of course those SynthDefs
won’t run yet, because we haven’t 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 “san-
ity checking” to your UGen class to ensure that the user doesn’t try to connect things
in a way that doesn’t 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 UGen’s FKHFN,QSXWV
method will be called. The default method defined 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. Let’s 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 — there’s
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
first argument to PXOWL1HZ). The LQSXWV variable is an array of the unit’s inputs, each
of which will be a UGen and thus will also have a UDWH variable. So the method com-
pares the present unit’s rate against its first input’s rate. It simply returns a string
if there’s a problem (returning anything other than nil is a sign of an error found
while checking input). If there’s not a problem, then it passes through to the default
FKHFN9DOLG,QSXWV method — if you implement your own method checking, don’t
forget to pass through to this check.
Many UGens produce output at the same rate as their first input — for example,
filters such as /3) or +3). If you look at their class definition (or their superclass, in
the case of /3) and +3) — an abstract class called )LOWHU), you’ll see that they call a
convenience method for this common case called FKHFN6DPH5DWH$V)LUVW,QSXW. Ob-
serve 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 don’t 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 audio-
rate data but is given only control-rate data may read garbage input from memory
that it shouldn’t be reading.
697 25 Writing Unit Generator Plug-ins

Returning to the )ODQJHU example created earlier, you may wish to add rate check-
ing to that class. In fact, since the Flanger is a kind of filter, you might think it sen-
sible 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 files which are distrib-
uted along with SuperCollider’s source code:
On Mac, the Xcode project file Plugins.xcodeproj is used to build the core set of
SuperCollider plug-ins. It’s 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
specifics.
On Linux, the scons project file SConstruct is used to build SuperCollider as a whole.
You can edit this file using a text editor to add your plug-in’s build instructions. Al-
ternatively, the “sc3-plug-ins” SourceForge project provides an SConstruct file
purely for building UGens — you may find it easier to start from that as a template.
On Windows, Visual Studio project files are provided to compile plug-ins, including
a UGEN_TEMPLATE_VCPROJ.vcprojtemplate file 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-in’s 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
UGen’s constructor function to perform the setting up of the UGen.
To produce sound, scsynth calls each UGen’s 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 block’s worth of audio-
rate values during 1 call. (Note: Demand UGens don’t quite fit 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 figure 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 file for SuperCollider’s plug-in
interface, SC_PlugIn.h. This is sufficient to include enough SuperCollider infrastruc-
ture 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 define 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 in-
herits 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 UGen’s functions, using the H[WHUQ& specifier so that the scsynth
executable is able to reference the functions using C linkage. In a given plug-in we
are allowed to define 1 or more UGens. Each of these will have 1 constructor
(“Ctor”) function, 1 or more calculation (“next”) functions, and optionally 1 de-
structor (“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 struct’s member variables appropriately. In this case we ini-
tialize 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 specified input. Here, we use
,1  — remembering that numbering starts at 0, this corresponds to the second
input, defined in the Flanger class file 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 unit’s struct. In our ex-
ample there’s only 1 choice, so we simply call 6(7&$/& )ODQJHUBQH[W . It’s 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 doesn’t 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 XQLW!UDWH
 IORDWIZGKRS XQLW!IZGKRS
 IORDWUHDGSRV XQLW!UHDGSRV
 LQWZULWHSRV XQLW!ZULWHSRV
 LQWGHOD\VL]H XQLW!GHOD\VL]H

 IORDWYDOGHOD\HG

 IRU LQWL LLQ1XP6DPSOHVL ^


  YDO LQ>L@

  'RVRPHWKLQJWRWKHVLJQDOEHIRUHRXWSXWWLQJ
   QRW\HWGRQH

  RXW>L@ YDO
 `

 XQLW!ZULWHSRV ZULWHSRV
 XQLW!UHDGSRV UHDGSRV
`

YRLGORDG ,QWHUIDFH7DEOH LQ7DEOH ^

 IW LQ7DEOH

 'HILQH6LPSOH8QLW )ODQJHU 
`

Figure 25.1
(continued)
701 25 Writing Unit Generator Plug-ins

to define multiple calculation functions and allow the constructor to decide which
one to use. This is covered later.
3. Calculate one sample’s worth of output, typically by calling the unit’s 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 con-
structors 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 process-
ing. In this example it doesn’t actually alter the sound — we’ll 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++ file is the ORDG function, called when the scsynth execut-
able boots up.
We store the reference to the interface table which is passed in — note that although
you don’t see any explicit references to IW elsewhere in the code, that’s 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 defines. 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 de-
structor, we would instead use 'HILQH'WRU8QLW )ODQJHU , which tells the server that
we’ve also supplied a destructor function named Flanger_Dtor. You must name your
constructor/destructor functions in this way, since the naming convention is hard-
coded into the macros.

So what is happening inside our calculation function? Although in our example the
input doesn’t 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 
,1  IORDW RXW 287  ; The macros ,1 and 287 return appropriate point-
ers for the desired inputs/outputs — in this case the first input and the first output. If
the input is audio-rate, then LQ>@ will refer to the first 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 first value in the location refer-
enced by ,1 . ,1  is exactly the same as ,1  >@.
702 Dan Stowell

We copy some values from the UGen’s struct into local variables. This can improve
the efficiency 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 we’re 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 prob-
lems 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 it’s possible that
they aren’t — 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 efficient. 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
can’t write the UGen efficiently 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 file on writing UGens
provides an example in which this ordering is important.)
Finally, having produced our output, we may have modified some of the variables we
loaded from the struct; we need to store them back to the struct so the updated val-
ues are used next time. Here we store the UDWH value back to the struct — although we
don’t modify it in this example, we will shortly change the code so that this may
happen.

The code in figure 25.1 should compile correctly into a plug-in. With the class file 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 doesn’t currently add any effect to the sound. But we can at
least check that it runs correctly (outputting its input unmodified 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 ac-
cessed 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 UGen’s 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
filtering, 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 flanging effect, we need a short delay line (around 20 milli-
seconds). 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 UGen’s 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 . (We’ve also added code to the calculation
function which reads and writes to the delay line, creating the flanging 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 YRLG SWU 57$OORF XQLW!P:RUOGQXPE\WHV


IUHH SWU 57)UHH XQLW!P:RUOGSWU

SuperCollider UGens allocate memory differently from most programs. Ordi-


nary 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 efficient 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 figure 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 UGen’s choice of calculation function is specified within the constructor rather
than being fixed. 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 first 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 float* pointer to input number index


287(index) A float* 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 UGen’s first 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 unit’s 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—
numBytes) analogous to realloc(pointer, 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 float 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 unit’s 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 file 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 oc-
curred. If you wish to provide trigger inputs, use this same convention.
The change from nonpositive to positive requires checking the trigger input’s 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 float 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 WULJ6WRUHFXUUHQWYDOXH³³QH[WWLPHLW
OOEHWKH
SUHYLRXVYDOXH

The sketch is for a control-rate trigger input, but a similar approach is used for au-
dio-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 first
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 in-
dex number of that buffer as a float 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
server’s 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 avail-
able via a float pointer EXI'DWD along with 2 integers defining the size of the buffer,
EXI&KDQQHOV and EXI)UDPHV. Note that the macro assumes the buffer index is the first
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, defined 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 specified in SC_RGen.h and provides functions for
random numbers from standard types of distribution: uniform, exponential, bilin-
ear, 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 fol-
lowing excerpt shows how to generate random numbers for use in your code:
5*HQ UJHQ  XQLW!P3DUHQW!P5*HQ
ҽRDWUҽ UJHQIUDQG $UDQGRPҽRDWXQLIRUPO\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 indefinitely, but often a UGen reaches the end of its useful
“life” (e.g., it finishes outputting an envelope or playing a buffer). There are 3 spe-
cific 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” flag to indicate that they’ve finished. Other UGens can
monitor this and act in response to it (e.g., 'RQH, )UHH6HOI:KHQ'RQH). See the Help
files for examples of these UGens. If you wish your UGen to indicate that it has fin-
ished, set the flag as follows:

XQLW!P'RQH WUXH

This doesn’t 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 calcu-
lating/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 ar-
guments it takes a pointer to the unit struct and an integer number of samples. You
can take advantage of this similarity to provide an efficient 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 efficient way for your UGen to produce silent output for the remain-
der of the synth’s execution.

25.5.11 Summary of Useful Macros

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

25.6 Specialized Types of UGen

25.6.1 Multiple-Output UGens

In the C++ code, writing UGens which produce multiple outputs is very straightfor-
ward. 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. Let’s look at the 3LWFK UGen to see how it’s 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 fixed, 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, specified using the
QXP&KDQQHOV argument. The init method for 3OD\%XI takes the QXP&KDQQHOV input
(i.e., the first value from the list of inputs passed to init) and specifies that as the
number of outputs.

25.6.2 Passing Arrays into UGens

25.6.2.1 The class file


As described earlier, the PXOWL1HZ method automatically performs multichannel ex-
pansion 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. Here’s how the class
file 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 con-
catenation operator, in essence appears as a set of separate input arguments rather
than a single array argument.
Note that 5HFRUG%XI doesn’t know in advance what size the input array is going to
be. Because of the array flattening 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 begin-
ning, 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 per-
haps 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 num-
ber 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 mem-
ber 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 file


Writing the class file 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 files in 2 ways:
1. The first argument to PXOWL1HZ (or PXOWL1HZ/LVW) is
GHPDQG
.
2. They implement a single class method, QHZ, rather than DU/ NU/ LU. This is be-
cause 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 spec-
ifying 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 can’t make assumptions about regular timing, such as the assump-
tions 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 (pro-


duced by the ))7 UGen). They don’t operate at a special “rate” of their own: in real-
ity 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 file


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 files 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 first 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 pro-
cedures 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
file FFT_UGens.h, which defines some PV-specific 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 “fired,”
then they access the buffer(s) and continue with the rest of the calculation function;
but if the FFT chain has not “fired” 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 func-
tion 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 first input to the UGen and cre-
ate 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 first chain: it takes the FFT chain indicated by the first 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 it’s what we’ll
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 fre-
quency (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 be-
tween. The number of bins in between is given by the value QXPELQV, which is pro-
vided 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 com-
ponent as the “first” bin in the frequency-domain data. However in these structs,
because the DC component is represented differently, ELQ>@ is actually the first non-
DC 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 don’t 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 sim-
plest 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 Xcode’s debugger (which is actually gdb with a
graphical interface) to perform tasks such as pausing the process and inspecting val-
ues of variables. On Mac, if you use the debugger to launch SuperCollider.app, re-
member 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
system’s 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$OORF’ed, 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 re-
veal 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 ex-
pected limits: make sure you 57$OORF the right amount of space for what you’re
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 specifics of the code in
question. However, we can suggest some optimization tips for writing SuperCollider
UGens. The efficiency/speed of execution is usually the number-one priority, espe-
cially 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 first limits you to 40 simultaneous instances, while the sec-
ond will allow up to 66; a 65% increase. Imagine doing your next live performance
on a 4-year-old processor — that’s essentially the effect of the less efficient code.
Avoid calls to “expensive” procedures whenever possible. For example, floating-
point 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 find an integer power of 2, use
bit shifting (  Q) rather than the expensive math function (SRZ  Q ). Other
expensive floating-point operations are square-root finding and trigonometric oper-
ations (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 filter UGen will take a user parameter (such as cutoff
frequency) and use it to derive internal filter coefficients. If you store the previous
value of the user parameter and use this to check whether it has changed at all —
updating the coefficients only upon a change — you can improve efficiency, since of-
ten UGens are used with fixed or rarely changing parameters.
One of the most important SuperCollider-specific 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 find that a certain update procedure is expensive and won’t usually be
required to run at audio rate, it may be preferable to update only once during a cal-
culation 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 control-
rate calculation can avoid the looping required for audio-rate calculation and typi-
cally produces a much simpler calculation as a result. There is a maintenance
overhead in providing these alternatives, but the efficiency gains can be large. In this
719 25 Writing Unit Generator Plug-ins

tension between efficiency and code comprehensibility/reusability, you should re-


member the importance of adding comments to your code to clarify the flow 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 ear-
lier, '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 first 99
times it’s 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 unit’s 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, Apple’s vDSP library can improve speed by vectorizing certain calculations.
If you make use of this, or other platform-specific libraries, remember the consider-
ations of platform independence. For example, use preprocessor instructions to
choose between the Mac-specific code and ordinary C++ code:
LI6&B'$5:,1
7KH0DFVSHFLҼFYHUVLRQRIWKHFRGH LQFOXGLQJHJY'63IXQFWLRQV
HOVH
7KHJHQHULFYHUVLRQRIWKHFRGH
HQGLI

6&B'$5:,1 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 mainte-
nance overhead, because you need to make sure that you update both branches in
parallel.

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 distrib-
ute 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 copy-
right in order to GPL-license a piece of code.) Practically, this has a couple of impli-
cations:
• 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 doesn’t offer an exhaustive list of all that’s possible, but it provides you
with the core of what all UGen programmers need to know. If you want to delve
deeper, you will find 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 SuperCollider’s core UGens or develop-
ing something very specialized, you’ll find 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 flexible
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 you’ve
finished 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 we’ll simply refer to scsynth as “the server.” “The cli-
ent” usually refers to sclang or any other program sending OSC commands to the
server. Although the text focuses on the server’s real-time operating mode, the infor-
mation presented here is equally relevant to understanding scsynth’s non-real-time
mode. As always, the source code is the definitive reference and provides many inter-
esting details which space limitations didn’t 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 find that the
source file, the class name, or both may have an SC_ prefix. I have omitted such
prefixes 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
first 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 scsynth’s coding style. Fol-
lowing 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 communi-
cations using queues, and how scsynth fulfills real-time performance constraints
while executing all of the dynamic behavior offered by the domain model. The final
section briefly highlights some of the fine-grained details which make scsynth one of
the most efficient software synthesizers on the planet. scsynth is a fantastically ele-
gant 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 consid-
ered member functions are typically global functions in scsynth. These are declared
with names of the form 6WUXFW7\SHB0HPEHU)XQFWLRQ1DPH 6WUXFW7\SH V>@ ,
where the first 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 vir-
tual functions. A number of cases of what can be considered inheritance are imple-
mented by placing an instance of the base class (or struct) as the first 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 significant is the lack of an Application Binary Interface (ABI) for C++,
which makes dynamically linking with plug-ins using C++ interfaces compiler-
version-specific. The avoidance of C++ constructs also has the benefit of making all
code operations visible, in turn making it easier to understand and predict the per-
formance and real-time behavior of the code.
The separation of data from operations and the explicit representation of opera-
tions 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 pur-
poses eases the burden on memory allocation by ensuring that dynamic objects be-
long 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 Inside scsynths

26.2 The scsynth Domain Model

At the heart of scsynth is a powerful yet simple domain model which manages dy-
namic allocation and evaluation of unit generator graphs in real time. Graphs can be
grouped into arbitrary trees whose execution and evaluation order can be dynami-
cally modified (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 significant 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 files for further information about the roles of these classes and the exact op-
erations which can be performed by them.
:RUOG is the top-level class which (with the exception of a few global objects) ag-
gregates 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 configuration parameters, which are usually passed to scsynth on the com-
mand line.
scsynth’s main task is to synthesize and process sound. It does this by evaluating a
tree of dynamically allocated 1RGH instances (near middle-left of figure 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 specific 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 exe-
cutes 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 con-
tainer for a linked list of 1RGH instances, and since *URXS is itself a type of 1RGH, arbi-
trary 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) sim-
ply iterates through the Group’s contained Nodes, calling each Node’s 1RGH&DOF)XQF
in turn. Although most code deals with Nodes polymorphically, the 1RGHP,V*URXS
field 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 significant 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, filtering, 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 efficiently allocated from the Graph’s mem-
ory block and evaluated by iterating through a linear array containing pointers to all
of the Graph’s Units. Each 8QLW instance provides a 8QLW&DOF)XQF function pointer
to compute samples, which affords the same kind of flexibility as 1RGH&DOF)XQF de-
scribed above. For example, many Units implement a form of self-modifying code by
switching their UnitCalcFuncs on the fly to execute different code paths, depending
on their state.
Graphs are instantiated using a *UDSK'HI (Graph Definition), which defines the
structure of a class of Graphs. The *UDSK'HI type implements the SuperCollider con-
cept 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 efficiently 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 specifies 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 intercon-
nection graph is represented by instances of ,QSXW6SHF and 2XWSXW6SHF. This inter-
connection 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 defined by
a GraphDef is not modified by scsynth.
scsynth’s 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 server’s 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 defined 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 simplified by calling the macros defined 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 significance 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 mem-
ory 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 in-
cludes considerations of threading, memory allocation, and interthread communica-
tions. scsynth is a real-time system, and the implementation is significantly influenced
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

scsynth’s primary responsibility is to compute blocks of audio data in a timely man-


ner 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 nec-
essarily 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 con-
straints 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, pre-

computing 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 attempt-
ing to lock a mutex or wait on a file 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 con-
sidered 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 con-
text 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 sc-
synth can read and write sound files 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 fixed-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-configurable nonresizable data structures. Exhaustion of such data

structures typically results in scsynth operations failing.

The first point is possibly the most important to grasp, since it defines the perva-
sive mechanism for synchronization and communication between non-real-time
threads and the real-time context which computes audio samples. When a non-real-
time 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 communica-
tion 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 mes-
sage queue objects. The padlock indicates a lock (mutex), and the black circle indi-
cates a condition variable. Full arrows indicate synchronous function calls (invocation
of queue-member functions), and half arrows indicate the flow 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-specific 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 with-
out the use of locks.
Referring to figures 26.2 and 26.3, the dynamic behavior of the server can be sum-
marized as follows:

1. One or more threads listen to network sockets to receive incoming OSC mes-
sages 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 gen-
erate 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 non-
real-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 filling 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 execut-
ing 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 notifica-
tions of server events. Performing the enqueued notification 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. How-
ever, 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 server’s real-time be-
havior. Zooming in to a finer level of detail reveals many interesting mechanisms
which are worth the effort to explore. A number of these are discussed in the sec-
tions 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 reimple-
mentation of Doug Lea’s 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 doesn’t 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 fixed-
size 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 there’s guaran-
teed to be only 1 reading thread and 1 writing thread, and you’re careful about how
the pointers are updated (and take care of atomicity and memory ordering issues)
then it’s 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 real-
time 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, let’s consider the basic mechanism, which involves posting )LIR0VJ in-
stances 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 fields
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 don’t require
this freeing mechanism, such as P7ULJJHUV, P1RGH(QGV, and P'HOHWH*UDSK'HIV. These
queues carry specialized notification 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 efficient to create
separate specialized queues using message types large enough to hold all of the nec-
essary 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 imple-
mented by the FifoMsgFuncs defined 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 pro-
cess. 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, poten-
tially providing a fifth stage of execution. SequencedCommands are used for opera-
tions which need to perform some of their processing in the NRT context. At the
time of writing, all 6HTXHQFHG&RPPDQG subclasses were defined 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 defined at the time of writing.)
To provide a concrete example of the SequencedCommand mechanism, we turn to
the Help file 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 allo-
cated, loaded and freed while synthesis is running, even while unit generators are
using them.” Given that a SndBuf’s sample storage can be quite large, or contain
734 Ross Bencina

Table 26.1
Subclasses of 6HTXHQFHG&RPPDQG Defined 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 floating-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 file, :RUOG actu-
ally 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 modified 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 SndBuf’s 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 context’s P6QG%XIV array. This process is summarized in
figure 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, figures
26.5 through 26.8. The exact function parameters have been simplified 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 filled with data from a sound file
is as follows:
EBDOORF5HDGEXIQXPSDWKVWDUW)UDPHQXP)UDPHV
735 26 Inside scsynths

Figure 26.4
Overview of multithreaded processing of the EBDOORF5HDG command.

Stage 1 (see figure 26.5): The real-time context processes an OSC packet contain-
ing 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 figure 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: first it calls :RUOGB*HW157%XI , which re-
trieves a pointer to the NRT copy of the 6QG%XI record for FPG!P%XI,QGH[. Then it
opens the sound file 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 file
information and to allocate a new sample data array. The data are read from the file
into the sample data array and the file 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 figure 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 figure 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 notification message back to the client who initiated the Buffer allocation. Fi-
nally, 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 mes-
sages 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 , fills it with an OSC packet (for example, by reading from a network
socket), and then calls 3URFHVV26&3DFNHW . 3URFHVV26&3DFNHW takes care of en-
queuing 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 efficient but don’t strictly conform to the OSC specification. 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 real-
time context to enqueue notifications 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 first place and have operations fail if no more space is available. This fixed-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 context’s 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
1RGHV the server can accommodate and the speed of 1RGH lookup as P1RGH/LE be-
comes full. The sizes of many of these fixed-size data structures are configurable in
:RUOG2SWLRQV (in general, by command line parameters), the idea being that the de-
fault values are usually sufficient, but if your usage of scsynth causes any of the de-
fault limits to be exceeded, you can relaunch the server with larger sizes as necessary.

26.4 Low-Level Mechanisms

As may already be apparent, scsynth gains much of its power from efficient imple-
mentation mechanisms. Some of these fall into the category of low-bounded com-
plexity 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 efficiently, 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 briefly 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 im-
proves the efficiency 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 don’t guarantee constant time perfor-
mance in the worst case, when combined with a good hashing function (Wang,
2007) they typically provide close to constant performance so long as they don’t 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 filled during a control cycle:
740 Ross Bencina

:RUOG maintains the P%XI&RXQWHU, which is incremented at each control cycle. When
a Unit writes to a bus, it sets the corresponding touched field (for example, in the
P$XGLR%XV7RXFKHG array for audio buses) to P%XI&RXQWHU. Readers can then check the
touched field to determine whether the bus contains data from the current control
cycle. If not, the data doesn’t need to be copied and zeros can be used instead.
• Delay lines typically output zeros until the delay time reaches the first input sam-

ple. 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 UnitCalc-
Func during the startup phase. For example, %XI'HOD\1BQH[WB] outputs zeros for
the first 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 Unit’s parameters. For example,
%LQDU\2S8JHQVFSS defines 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.

This concludes our little journey through the wonderful gem that is scsynth. I in-
vite you to explore the source code yourself; it has much to offer, and it’s free!

References

Gamma, E., R. Helm, R. Johnson, and J. Vlissides. 1995. Design Patterns: Elements of Reus-
able 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): 61–68.
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 definitions of terms, see the syntax appendix.

12-Tone Matrix, 34–35 Binary, 65, 133–134


numbers, 640, 642
Abstraction, 210–211. See also chapter 7 operators, 12
Additive Synthesis, 3, 6, 34–37, 128 Binaural, 420–422, 560, 582–586. See also
AIFF, 25, 195, 254, 483–485 chapter 19
Algorithm (algorithmic), 122, 420, 460, 560 Bipolar, 21, 42–46, 57, 65
composition, 599 Boolean, 31–33, 65
inside a method, 161–162 BPF (Band pass filter). See Filter
as pattern, 607–608 Buffer, 24–29, 61, 76, 151, 184, 200, 205,
for pitch extraction, 441–442 367–372, 480–484, 710, 725, 731–734,
synthesis, 385, 390, 644, 653 737
Ambient Lights (project), 118 Bus, 25–36, 43, 57–60, 80, 538, 550
Ambisonics, 424–425 Byte Code, 146–147, 676–679
Analysis
FFT, 431 C++, 55, 120, 128, 178, 240, 357, 483, 578,
real time, 440–446 659, 697–704, 712–723, 741–742
signal, 61, 65 Carrier (phase modulation), 15–16, 20,
UGens, 122 45
Arduino, 120–124 Cents, 508–509
Arguments, 6–10, 132–133, 148–149 Char, 287, 742
Array, 11–14, 23, 28–36, 56–57 Class (classes), 56, 128–130, 168–172
indexing, 238–240 as object models, 241–243
literal, 742–744 tree, 173
nested, 89 writing, 694–695
ASCII, 115, 121, 128, 165, 362 Clock (class), 83, 87, 101, 219, 228
Association, 163, 744 AppClock, 83, 234, 246, 282, 626
Audio rate, 17, 42, 56, 196 SystemClock, 67, 83–84, 549, 626
TempoClock, 83–84, 197–202, 645
Balancing enclosures, 12 Cloud (CloudGenerator), 258, 261, 478–
Beat Tracking. See Machine listening 480
746 Subject Index

Cocoa, 349, 356, 375 Enclosures, 12


CocoaDocument (see Document) Envelope, 18, 25, 47, 99, 343, 345, 414
Coding Environment, 55, 120–124, 166–167, 648
conventions, 659–660 variables, 25, 41, 139
networked live, 230 Evaluation (of code), 4, 7, 146
scsynth style, 722 Event, 180–182. See also Patterns; chapter 6
Collection, 12, 14, 28–29, 60, 87, 115, 118, note (keys), 184–189
128, 132, 134, 152, 162–165 as object models, 241–243
Comb (N, L, C), 61, 76, 80 PatternProxy, 220–221
Combinatorics, 230 protoEvent, 193–197, 202–205, 603–605
Comments, 10–11, 52, 719 streams, 220–222, 225–230
Compilation (compiler), 146, 659–660, triggering, 55
664–666 Extensions. See Libraries
Composition. See chapter 3 External devices. See chapter 4
DAW style, 53, 81, 93
object oriented (see chapter 18) FFT, 440–442
Compression, 62 Filter, 5, 17, 61, 68, 80
Conductor. See Patterns, conductor BPF, 122, 212
Constraints. See chapter 23 HPF, 80, 122
Control rate, 17, 26, 56, 79–80, 196 Klank, 36, 38, 50
ControlSpec, 277, 279 Lag (Lag2), 62, 69, 185, 212
Convolution, 417 LeakDC, 402
CPU (usage), 72–76, 264, 401, 718–719 LPF, 80, 397, 429, 692–693
Crucial library. See Libraries, crucial Median, 122
Csound, 61, 723 Ringz, 231–232, 346, 353
Cue Players, 91 RLPF, 3, 345
Flange, 695, 698, 703
DAW (Digital audio workstation) Float (floating-point), 11, 239
Composition (see Composition, DAW) Flow control, 160–162
DC (offset), 716–717 FlowLayout, 285
Debugging, 48, 55, 62, 108, 325, 361, 717 Fourier, 357, 417
Decorrelation, 428–436 Frequency modulation (FM). See
Delay, 61–62, 76, 79–80, 415–417 Modulation, frequency
Devices, external. See External devices FreeVerb, 68–70, 417, 419
Dialects, 635–637. See also chapter 23 Function, 11–14, 60, 143–144, 147
Dialogue (windows), 99, 151, 302 FunctionDef, 671
Dictionary, 139, 141, 163, 165 iterating, 152
Distortion, 62, 494 return, 130–131, 144
Document
Emacs, 373 Garbage collection, 659–660, 684–685
OS X GUI (Cocoa), 299–303 Gate, 18, 28, 151
Dot (receiver dot message), 10–11, 114, 130 Gestures, 97
Drag and Drop, 288 Granular synthesis, 64, 80, 197, 258, 432,
465–469. See also Microsound
Emacs (scel), 355–357, 366–374 client-side, 432–433
Encapsulation, 557–564 server side, 480–483
747 Subject Index

sound files, 489–490 JSCUserView. See GUI


wave sets, 490–500 Juggling, 395
Grouping. See Precedence
GUI (Graphical user interface). See also Key Tracking. See Machine listening
Platforms; chapters 9–12 Keyboard and Mouse, 286–287
cross-platform, 298–299 Keywords, 16, 132, 171, 741
dynamically generated, 295–297 Klank. See Filter
Emacs (see chapter 12)
JSCUserView, 319–321 Lag (Lag2), 62, 69, 185, 212
OS X, 274–276 LazyEnvir, 211, 215, 646
SCUser, 291–294 LeakDC. See Filter
static (singleton), 298 Libraries
SwingOSC (see chapter 10) C, 659
tuning, 529 chucklib, 603–607
windows, 349 crucial, 303
GVerb, 68–70, 417, 419 dewdrop_lib, 589–611
extensions, 55, 62, 79, 303, 546, 572
Harmonic spectrum. See Spectrum, Linux, 359–360
harmonic series platform specific, 719–720
HID (Human Interface Devices), 105–111 Windows (platform), 351
Linux, 365 quarks, 615
HierSch, 644–647 Linear. See Interpolation
History, 230–235, 243 Linux, 3–4, 11. See also chapter 12
HPF (High pass filter). See Filter ALSA, 363–365
Human Interface Devices. See HID introspection, 371–372
JACK, 362–363
IdentityDictionary, 163, 165–166, 183 Live performance. See chapter 20
If (statements). See Flow control ListPattern, 141
Inharmonic spectrum. See Spectrum, Literals, 129–130
inharmonic Localization, 385
Inheritance, 129, 168–171 Logical expressions, 33. See also ==, !=, >, <,
Instance methods, 130, 137, 139 >=, <=, and, or in code index
Instance variables. See Variables, instance Loop, 25–30
Interpolation, 56, 65, 68, 74, 76, 79–80 infinite, 153, 218
Interpreter, 180, 182, 205, 208–209, 240, LPF (Low pass filter). See Filter
246, 307, 679–685
variables, 140–141 Mac OS X. See Platforms
Introspection. See Linux, introspection Machine listening. See chapter 15
iPhone, 633 beat tracking, 450–452
Iteration, 28–31, 152–153 key tracking, 452
ixiQuarks, 614–619, 624–628 onset detection, 446–450
transcription, 452–453
JACK. See Linux, JACK Map (Mapping), 58, 65, 106, 108,
Japan. See chapter 22 122–124, 385–386, 401–405
Java, 128–309, 315–319, 326–329 Markov, 41
JITLib (Just In Time), 102, 480, 603, 648 Matrix, 12-Tone. See 12-Tone matrix
748 Subject Index

Max/MSP, 14, 156, 308, 375, 457, 660 OSC (Open Sound Control), 65, 89, 105,
Median. See Filter 114, 538, 737
Message (method), 104, 130–131
chains, 11, 130 Panning, 57, 62, 72, 385–386, 411–414
instance, 130, 137, 139 Parallel FX, 25, 28
nested, 8–12 Patterns, 189–195. See also Events; chapter
Method. See Message 6
MetaClass, 172 conductor, 200–201
Microsound. See Granular synthesis; chapter empty, 219
16 proxy, 215, 220–222, 226
MIDI, 30–33, 105, 111–114, 589, 592–593, recursive, 222
595, 608, 611 Phase, 56, 79, 128, 228
MIDIIn, 594–595 modulation (see Modulation, phase)
MIDIResponder, 595 spectrum decorrelation, 431, 435
Modulation, 79 Physical Model, 644
frequency, 15, 43, 385, 643 Pink Noise, 343
index, 16 Platforms
phase, 3, 15, 20–21, 29, 47 Linux (see chapter 12)
pulse-width, 120 Max OS X (see chapter 9)
Modulo (Mod, %), 18, 31 Windows (see chapter 11)
Mono (Monophonic), 14, 58, 411–412 Plug-ins. See chapter 25
Mouse. See Keyboard and Mouse Polymorphism, 87, 89, 104, 168, 237–238,
Multichannel (expansion), 14, 17, 57–58, 564, 637
205, 410–413 Precedence, 12, 37, 134
Precedence effect, 411, 437
Nesting, 8–12 Programming. See chapter 5
Networked live coding. See Coding, Primitives, 684–689
networked live Prototypes, 598–602
Nil, 662, 672–705 ProxySpace, 345
Node, 24, 28, 59–61, 184–185, 210–217, PySCLang, 352–353
221–222, 305, 316, 368
NodeProxy, 211, 228, 230, 404 QCD (quantum chromodynamics), 252–258
NodeProxyEditor, 228 Quantization, 197, 199, 228
Noise, 62, 68, 74, 79, 431 Quarks, 105, 359. See also ixiQuarks
Nyquist, 442, 716–717
Random
Object(s), 128 button, 265–266
layout, 660–664 collection (array), 132, 162
modeling (see chapter 8) number generators, 61–64, 668, 710
oriented composition (see chapter 18) patterns, 190
oriented programming, 127–129 parameter choices, 8, 13–14, 30–35,
Offset, 42–48 87–88, 90
Onset detection. See Machine listening pseudo, 80, 710
Open Sound Control. See OSC range, 31–32, 38, 261–262 (see also rrand
Operators, 12, 31, 133–134, 164 in code index)
Optimization, 61, 72, 74, 718 seed, 8, 69
OS X. See Platforms server side 68–69, 189–190
749 Subject Index

Ratios. See Tuning Sidebands, 20, 46


Rate Slider, 274–276, 280, 307–308, 311–314
audio (see Audio rate) Smalltalk, 207, 237–240, 269, 375, 570
control (see Control rate) Sonification, 236, 252–258. See also chapter
sample (see Sample rate) 13
Recursion (recursive), 153–155, 225–227, Spatialization. See chapter 14
614, 665 3D audio, 420–421
Receiver, 10, 130 Spectrum, 34, 61, 68, 80, 403, 405
Recording, 53, 93, 95–98 diffusion, 432–435
References, 142 harmonic series, 15–16, 28, 33–35, 41,
Reverb, 409, 417–420 45–47, 441, 528
Ringz. See Filter inharmonic, 34
RLPF (Resonant low pass filter). See Filter StartUp, 306–307, 340–341, 347–352,
Routine, 83–84, 87, 98, 102, 603–604, 607 366
Streams, 199–202. See also Patterns
Sample and Hold, 47, 49, 79 String, 11, 14, 23, 51, 361
Sample rate, 196, 445, 641, 698, 708–709, Subtractive Synthesis, 3, 5
734 Switch (statements). See Flow control
Scale (collection of notes), 28, 31, 34, 41, SwingOSC. See GUI
128, 167 Symbol, 12, 129
microtonal (see chapter 17) streams, 222
Scale (relative size), 43–48, 386, 608–610 Synth Definitions, 21–24, 179–180. See also
time, 489–490 chapter 6
scel. See Emacs Synthesis, non-real-time. See chapter 18
Scheduler, 638, 645
Scheduling, 83–87 Task, 29–32, 39–41, 83, 85–90
constraints, 635, 644–647 TaskProxy, 215–218, 262–265
sclang, 360–362 Tempo clocks. See Clock, TempoClocks
Scope, variable. See Variables, scope Transcription. See Machine listening
Score, 87–91, 371. See also chapter 18 Tuning. See chapter 17
scsynth, 55–56, 60, 65, 72. See also chapter equal, 505–509
26 just, 515–516
freeing, 197 odd Limit, 517
SCView, 291, 301, 303 poly, 519–521
SCWindow, 274 ratios, 515
Sequences (Sequencer), 38–41 tonality Diamond, 518
SerialPort, 117, 119, 121 unequal divisions, 515
Server, 55–69, 106, 109, 115–117, 305–307,
315–320, 326–328 UGen (Unit Generator), 10. See also chapter
node, 211–214 2 and chapter 25
options, 24, 59, 74 pseudo, 691
synthesis, 6, 10, 22–24, 52–53 UI. See GUI
window, 4, 57, 95 Unicode, 287
Windows (platform), 349 Unipolar, 44–46, 65
Shaper, 62 Unit Generator. See UGen
SharedIn, 349 UNIX, 115–116, 330, 360–361
Shout Window, 243–255 USB, 106, 120–121
750 Subject Index

Variables, 18–23, 31, 135–141


class, 139
environment, 139
instance, 137–140, 156–158, 166, 169,
202
interpreter, 140
pseudo, 140
scope, 25, 155–156
versus references, 142
VBAP (Vector based amplitude panning),
323–324
VEP (Virtual electronic poem), 575–587
View. See GUI
Voicer, 592–594
Voltage Control (VCO, VCF, VCA), 17

Wacom, 106, 109, 111, 124


Wave Field Synthesis, 425–427
Wave sets. See Granular synthesis
Wavetable, 163–164
Wii, 106, 109–110, 357
Window
as GUI, 274–275
shout (see Shout window)
Windows (platform), 3–4, 11. See also
chapter 12
Code Index

This index contains language elements of SuperCollider. While most terms are used through-
out 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.

Messages background, 107, 244–247


 , 33, 92, 313 bufnum, 26, 418
, 522, 709
 PRGPRGXOXV , 31–33 choose, 13, 32, 38–40, 100
, 42, 63, 94 class, 141, 172, 330
, 94, 135 clear, 210, 215–216, 221, 244–247, 250
 , 33 clip, 324, 402, 637, 641
!, 293, 564 clock, 204
, 31–33 close, 59, 63, 86, 120, 244–245, 251,
! , 33, 171, 174 255
coin, 32–33, 498, 502
abs, 66, 398 collect, 91–94, 120, 625
add, 22, 24, 39, 58–59, 63–64 connect, 111, 116, 364, 365
addAll, 134, 150 control, 27–28, 30, 296
adsr, 345 copy, 193, 262–263, 266–267
amp, 312, 486, 494 copyRange, 494
ampdb, 313, 455 count, 159, 176
and, 32–33 cpsmidi, 11, 66, 191
ar, 4–16, 58–59 current, 255, 260–267
asArray, 120, 204, 256, 529–530 curve, 321
asAscii, 120
asCompileString, 302 dbamp, 69, 70, 75–76
asInteger, 455 decorator, 86, 92, 98
asKeyValuePairs, 295 def, 147, 154, 296
asr, 531–532 default
asStream, 89, 101, 201, 220–221, 488, Server, 4, 133, 185
500–503 SwingOSC, 306–307
asString, 12, 42, 119, 134, 142 TempoClock, 197, 647
at (accessing elements of a collection), defer, 83, 176, 283, 296, 297
29–30, 113, 132, 203 degreeToKey, 513
audio, 30, 73 delta, 219, 606, 651
752 Code Index

destroy, 328–329, 335 info, 108


device, 118, 119, 347 init, 113, 364
disconnect, 117, 349 insert, 35, 51
discretize, 481–488 interpret, 134, 518, 652
dist, 321, 324, 430 ir, 69, 259, 388, 391
do, 30, 64, 69–76 isClosed, 245, 251
doOnce, 67, 277 isEmpty, 362
drop, 247, 249, 387 isFloat, 33
dump, 140, 241, 346 isInteger, 32, 33
dup, 11, 13, 77, 78 isKindOf, 161, 295, 567
duration, 553, 605, 609 items, 268, 289, 290

embedInStream, 193–194 key, 267, 552, 554, 651


env, 486, 551 kr, 4–6, 10, 14–17
envir, 261, 266
error, 257, 329 lag, 397, 399, 402
even, 33 latency, 204, 260, 485
explin, 277, 479, 640 lfo, 551, 559–561
exprange, 64, 216, 231–232 linen, 26, 388, 605
linexp, 191, 198, 277
fadeTime, 216, 233, 487 linlin, 471, 515, 524
fill linrand, 434, 476, 488
Array, 33, 37, 50, 63, 70 load, 539, 544, 547, 548
Mix, 6, 97, 616–617, 625 loadCollection, 434
findBy, 107–108
font, 145, 244–247 make, 203, 404, 471
for, 152 map, 28, 216, 262
forBy, 152 max, 6, 30, 35–37, 159, 174–176
fork, 85, 138, 145 midicps, 11, 13, 67, 69, 85
format, 313, 335 midiratio, 479, 506, 556
free, 19, 23, 58–59, 63 min, 49, 325, 455
freq, 476, 551, 555–556 mod (%), 18, 24–25
front, 83, 107 mouseDownAction, 289, 292, 324
mouseUpAction, 324, 620
gap, 448, 501, 625
get, 328, 332, 387, 388, 455–456 newMsg, 541–548
getDate, 600 next, 66, 88, 101
getn, 447, 448 nextLine, 266–267, 297
getPaths, 99, 302 node, 312, 329
global, 295, 296 nodeID, 295
globalKeyDownAction, 302 normalizeSum, 321, 531
gui, 317, 595, 596 not, 257, 292
notEmpty, 455, 458
id, 326, 328, 332, 333 noteOff, 113
if, 31–33, 38–39, 92, 257 noteOn, 111, 113
includes, 32 numChannels, 26, 329, 335
index, 644 numFrames, 26, 335
753 Code Index

odd, 32, 33 send, 233, 388, 454


onClose, 92, 145, 153 sendBundle, 116, 204, 259–260
option, 346, 410, 415 sendCollection, 388, 481–488
or, 33 sendMsg, 115, 333, 349, 471
set, 23, 48–49, 64, 140, 145–146
path, 26, 335 setn, 350, 422, 649
pause, 86, 95, 222 setStarttime, 553
perc, 24, 39, 48–50, 82 signalRange, 44, 494
permute, 13 silent, 221
phase, 716 sine, 198, 259, 433
play, 13–19 slice, 256, 257
plot, 11, 13, 35, 351 softclip, 38, 398, 402
poll, 42, 44, 63 source, 215
pop, 210, 214–215 sourceCode, 154
pos, 321 squared, 130, 239, 398
post, 29, 32, 35, 39–40 standardizePath, 538–547
postln, 11 start, 32, 40, 88
pow, 18, 132–133 startTime, 553, 566
prClose, 333 state, 86, 137, 150–151, 266
put, 132, 203, 257, 261–262 stop, 32, 40, 86–95
putAll, 119–120, 219, 267 stream, 101, 512
pyramid, 150 string, 150, 176, 244–247, 308
stringColor, 246, 252
quant, 228, 260, 263 sum, 59, 122, 149
sum3rand, 710
rand, 8, 11–13 swing, 326, 330, 350, 376
range, 28, 45, 64, 69, 70
ratio, 533 tempo, 84, 88, 204, 647
read, 25, 91, 99 trace, 224, 225
reciprocal, 64, 78, 96, 145
record, 95 uid, 113, 365
register, 302, 329
release, 69–78, 85–94 value, 84, 108, 119
render, 183, 195 valueArray, 171
reset, 87, 101, 158–159 valueEnvir, 167
reverse, 13 view, 150–162, 244–247
rotate, 13 visible, 285
round, 11, 66
rrand, 71–76, 162 wait, 30–33, 69, 85
run, 69, 350 while, 152
window, 59, 63, 250
sampleRate, 335, 418, 433 wrap, 40
sched, 67, 83–84 wrapAt, 31–35, 202
schedAbs, 647 wrapPut, 39–40
scope, 13, 58, 63 write, 538
scramble, 13, 220–221, 257
select, 295, 620 xrand, 142, 153
754 Code Index

Classes FSinOsc, 259


Allpass (N, L, C), 29, 50, 416 Function, 83, 87, 149, 155–159, 171
AppClock, 83, 246, 334
Array, 150, 391 GrayNoise, 133
GUI, 245, 247, 250, 266
BeatTrack, 444, 450, 451
BiPanB2, 426 Harmonics, 164
Blip, 14, 241 HPF, 122, 451, 696
BPF, 212, 397
BrownNoise, 175, 216 IdentityDictionary, 743
BufAllpass (N, L, C), 416 Impulse, 63–64, 77–78, 231–233, 350,
BufChannels, 708, 710 353
BufComb (N, L, C), 415 In, 26–30, 73, 422, 591
BufDelay (N, L, C), 415 IRand, 24, 68–69
BufDur, 487, 490
Buffer, 95, 388, 507 KeyCodeResponder, 287
BufFrames, 198, 418, 484 KeyState, 106
BufRateScale, 91, 198 KeyTrack, 444, 453
BufRd, 56, 61, 496 Klank, 36, 38, 50
BufSampleRate, 491, 547
Bus, 27–28, 59–61, 447 Lag (Lag2), 69, 212
Latch, 49
Char, 107, 119, 129, 663–667 LeakDC, 402
Clip, 324, 402, 637, 641 LFClipNoise, 26–27, 212, 216
CmdPeriod, 67, 277, 368, 626 LFDNoise1, 140
CocoaDocument, 299 LFDNoise3, 69
Comb (N, L, C), 10, 76–78 LFNoise (0, 1, 2), 5–6, 13–14, 64, 66, 69,
Compander, 63 70, 140
Convolution, 419 LFPulse, 28, 43–45, 215
LFSaw, 15, 43, 346, 351
Decay, 63, 77–78, 212, 388 LFTri, 17–20, 64, 145
Delay (N, L, C), 63, 415–416 Limiter, 62–63
DetectSilence, 39 Line, 388
Dialog, 99 Linen, 17–19, 91, 97, 180, 197
Dictionary, 202, 744 LinExp, 191, 198, 277
Dust, 5, 19, 63, 70, 97 LinPan2, 413, 414
LinXFade2, 413
EnvGen, 24, 26, 64, 69, 133, 138 LPF, 397, 429, 451, 458
Environment, 139–142, 166–167, 239
Event, 179, 204, 239, 311–312 MIDIIn, 111, 364, 458
EventStreamPlayer, 101, 197, 201, 371 MIDIOut, 111, 364
ExpRand, 5, 8, 13, 68, 153 Mix, 35–38, 458, 616, 625
MoogFF, 61
FFT, 431, 434 MouseX (MouseY), 15–16, 19–21, 106,
FlowLayout, 159, 266–267, 350 122, 389
Free, 19, 49, 58, 63 MultiSliderView, 447, 623, 625
755 Code Index

Ndef, 211, 215, 228–233 PriorityQueue, 731, 738


Nil, 5, 39, 85, 111, 136 ProxyMixer, 229, 345, 346
Node, 295 ProxySpace, 209, 229, 240, 396, 648
NodeProxy, 345 PV_BrickWall, 715
NoteOnResponder, 111, 112, 348 PV_Copy, 431, 434
NRand, 68 PV_Diffuser, 431
PV_HainsworthFoote (PV_JensenAndersen),
OffsetOut, 180, 198, 217, 231, 259 444, 446
OSCpathResponder, 295, 328 PV_MagMul, 434, 715
OSCresponder, 111, 116, 122, 251, 449,
455 QuadN, 389
OSCresponderNode, 65, 67, 116
Out, 22–24, 27–30, 58–59, 63–78 Ramp, 402
Rand, 24, 75, 217, 625
Pan2, 6, 11, 62, 180, 231–232 RandID, 69
Pan4, 413 RandSeed, 8, 69
PanAz, 62, 388, 402, 413, 423 RangeSlider, 267
PanB, 62, 73, 426 RecordBuf, 712–713
Patterns Rect, 86, 92, 98, 107, 129–131
Pbind, 101, 103, 162, 182–183, 189–198 ReplaceOut, 60, 73
Pbrown, 103, 189, 192 Resonz, 69–70, 95, 97
Pdef, 103 Ringz, 231, 232, 346, 353, 649
Pfunc, 190, 605, 609 RLPF, 5, 345
Pkey, 191, 192, 596, 605 Rotate2, 413–414
Pmono, 189 Routine, 69, 620
Ppar, 189, 222, 520, RunningSum, 122, 444, 454–455
Pproto, 205
Prand, 101, 162, 190, 192, 223, 227, 231, SampleRate, 388, 391
647 Saw, 345, 369, 702
Prout, 190, 607 SCButton, 275, 285, 299
Pseq, 39, 87–89, 181–183, 189–194 SCCompositeView, 282, 285
Pseries, 227, 595, 606–609 SCPen, 291
Pshuf, 223 SCSlider, 277, 281
Pspawner, 183 SCTextField, 289
Pstep, 190, 192, 194 SCUserView, 291–294
Pstutter, 190, 606 SCWindow, 83, 276–285
Psym, 222 SelectX, 397, 414
Ptime, 606, 609 SelectXFocus, 414
Ptpar, 194 SendReply, 68, 122
Ptuple, 221 SendTrig, 65, 66, 67, 122, 283
Pwhite, 189–191, 223–225, 231–232, 345 Server, 4, 52, 58–63, 295, 507
Pxrand, 87, 89, 101, 345, 606, 609 ServerOptions, 540, 545, 547, 549
PinkNoise, 344, 388, 402, 424 SinOsc, 5–7, 30, 35–37, 56–59
PlayBuf, 25–30, 61, 91, 95, 198 Slider2D, 350
PMOsc, 15–16, 21–24, 644 SoundFile, 195, 329, 335
PopUpMenu, 268 SoundIn, 65, 122, 418, 440, 445
756 Code Index

Splay, 414
SplayAz, 414
StaticText, 176, 268, 617–619
StereoConvolution2L, 417, 419, 422
SystemClock, 67, 83–84, 549

TabletView, 109
Tdef, 215–220
TempoClock, 83–84, 88, 185
TExpRand, 591
TGrains, 25, 64, 480–482
TIRand, 68–69
TRand, 17, 19–21
TToggle, 293

You might also like