I recently contributed to the sound component of a major art
installation in Croatia, the vast majority of which was done in
SuperCollider. On the sound side, it was a fairly large scale work,
taking the form of a 2 hour composition, some parts of which were
very precomposed and other parts of which were generated on the
fly. We were playing sound back to a 40 channel array of speakers,
distributed over an area maybe 200mx200m, from two computers (each
handling 20 of the channels). We used a wide variety of different
methods to diffuse sound through the space, some very systematic
(equal-power panning over adjacent speakers, over a large area),
some very arbitrary (i.e. play something in these three speakers
over there). One of the biggest challenges was combining the
variety of sound sources, diffusion techniques, gestures, etc. into
a single compositional framework that was flexible and easy to work
with. The entire project was probably in the range of 8000-10000
lines of code, and used a large variety of the various toolkits and
paradigms available in SuperCollider.
We had plenty of successes and failures at every juncture and in
every part of the project - I tried to keep some notes on the
roadblocks, and the tricks and successes we encountered along the
way, so I thought I'd share them. Since I'm not aware of too many
other SuperCollider projects on this scale, I hope that these
observations can generate some discussion, be a driver for making
good changes to SC, and help anyone else who is attempting a big
project like this in SC.
1. Debugging!
Debugging in SC is difficult at best - lots of people have made
this observation. If I had to guess, I would say roughly 50% of our
time on the project was spent debugging code and fixing problems.
With some basic debugging tools - an interactive debugger and
simple inspection of objects running on the server, this time could
have been cut down by a factor of five or ten. That's a lot of time
- period. I would say, for me, this is the #1 priority for SC right
now - far outweighing any additional UI functionality, new UGens,
etc etc.
2. Server execution order is tough to manage
Most of our problems on the server side came from execution order
problems. Some particular instrument of ours might require 5 or 6
synthdefs, the creation of buffers, busses, groups, and nodes
inside those groups, setting/mapping values for those synths/
groups. These things must be initialized in an exact order in order
for them to work correctly, and it can be maddening to figure out
exactly where things are going wrong. We got very good at solving
these problems by the end, but even then there was a lot of black
magic involved (i.e. make this synth, wait two seconds, make this
other one, wait one second, set parameters on both). There are
partial ways to solve these problems (Server.sync, completion
actions, sticking things in a routine), but I would venture to say
that SC is missing a simple, paradigmatic, guaranteed way of
handling this. I would love to hear from folks about ways they
handle this, and weigh in on what could be done about making it
easier.
3. SC has great libraries and abstractions, but poor compatibility
between them
We made extensive use of core SC objects like Node, Bus, Buffer,
etc., JITLib objects, Josh's amazing Ctk library, Tasks, ProcMods,
raw server messages. All of these things are very useful, for very
different things, but getting them to talk to each other can be
tough, and there's very little standardization. If I do aSynth.set
( \inputBus, aBus), does it turn my bus into a bus number? Can I
pass in a Bus to a CtkSynth? Can I pass a CtkBus into a Synth.set?
Can I make a NodeProxy that points to an existing CtkAudio bus?
Getting all these things to work together easily is not a big
problem, but it would require SC users and devs to get together and
agree on a common paradigm for handling the basic server objects.
In the case of our project, a bit of standardization on this front
would have helped immensely - do other people run into these
problems? Would this be a worthwhile goal to work towards with SC?
4. Hard limits on Server resources
Busses, wirebufs, memsize, synthdefs, rgens, etc. In the course of
our project, we hit the boundaries of almost all of these hard-
coded limits. The result was always disastrous to whatever we were
doing (there's no way to elegantly recover w/o rebooting the
server). We'd up the limit by some arbitrary amount, and then pray
that we didn't hit it again. I realize that there are efficiency
reasons for limiting each one of these - however, since the
consequences of hitting a limit are a server restart (meaning, in a
realtime environment, your performance/installation/whatever is
hosed), I think it would be good to go through these and determine:
(a) is there a reason this resource needs to have a hard limit
specified at launch time (i.e. if the user needs more buffers, is
there a way we could up the limit and allocate more automatically w/
o restarting the server)
(b) If there is a reason we can't do realtime allocation,
should we think about increasing these limits? Computing resources
are cheap these days.
5. Server crashes
There are still a few easy, guaranteed ways to crash the server. We
experienced users know what they are, and avoid them. But, for my
own part, I've never actually written a bug or attempted to fix any
of these (probably simple) things.
6. BroadcastServer rocks
We were able to develop an extensive framework of object, synths,
etc. for this piece using single machines, and then later on down
the road (far later than we should have, actually), start worrying
about sharing them between two machines and two servers. This was
accomplished with BroadcastServer, which did 95% of what we needed
right out of the box, and very successfully. I don't know who wrote
this, but thanks.
7. Node.trace rocks
Wow, I can't believe I never knew about this before. If it could
display it's data in a slightly more parse-able format, it would be
even better.
8. A nice trick with NodeProxy
We made extensive use of NodeProxy's for building simple
crossfadable presets that are /very/ extensible. Suppose you've got
a synth with 4 parameters - you want to be able to set them simply
at creation time, but also crossfade them between different states,
or even give them complex behaviors, w/o having to restart the
synth. Rather than using the args of a synthdef, we grabbed these
from an In.kr:
#amp, freq, decay, weirdness = In.kr( controlIn, 4 );
(where controlIn = NodeProxy.bus)
Though you could also use Control.kr() and then mapn the
NodeProxy.bus to those args
Now, you can assign fixed states to your synth by setting the
NodeProxy:
n = NodeProxy.new( server, \control, 4 );
n.source = [ 1, 440, 0.1, 500 ]; // corresponding to #amp,
freq, decay, weirdness
You can set fadetime (n.fadeTime = 10) to smoothly fade between
states , and even have the option of building much more complex
behaviors for each parameter (you can fade between these as well):
n.source = { [1, 440, 0.1, SinOsc.kr(0.1, 0, 500)] };
n.source = { var amp, freq, decay, weird;
amp = 1;
freq = EnvGen.ar( Env.sine(10)+300 );
decay = 0.4;
weird = LFNoise2.kr(0.1, 10, 100);
[ amp, freq, decay, weird ]
}
This is extremely powerful, and comes practically for free with
NodeProxy.
9. Multi-speaker simulation
Since we had probably less than 8 hours total to rehearse in the
actual space, we build a simulation of our 40 speaker setup, that
could be listened to virtually, with headphones or an ambisonic
array. It's fairly open ended - you can place speakers and a
listener position, and get a good idea of the sound at that point
(w/ distance scaling, delay, panning, etc etc). If there is
interest, I could share this as a Quark.
10. Version control
Keeping track of different versions of things, floating around
between the 3 of us writing code and our 3+ computers, or even
different versions of things on my own machine. I'm currently
working on an SC interface to Bazaar for local versioning and file
management on my own system - BZR is great for small scale projects
on your own machine, or shared between a few computers/people, and
plays nicely with SVN, so things could be exported as Quarks as
well. I could share this if there is interest.
I hope this long-winded rant doesn't sound too much like
complaining - we were able to achieve some pretty staggering things
that would NOT be possible in any other environment than
SuperCollider, period. A lot of this is due to great functionality
put in place by people on this list, so for that, thanks.
- Scott