[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[sc-users] Notes and observations from a large SC project



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