Endlessly repeating...

A flexible OSC responder

17 February 2011

A lot of my ideas involve generating musical information (notes, triggers, continuous control data) in one language (I'll refer to this as the controller for convenience) and then passing that to SuperCollider to make some noise with. That approach raises the question of where to put the intelligence in that system. Either the controller processes everything into musical information and SuperCollider acts as a 'dumb' synth or the controller can send a load of numbers without semantic meaning to SuperCollider which can then figure out what to do with them. To my mind, a logical way to choose which system to use is to look at whether you want to change the controller setup or the SuperCollider setup more often. If you're changing the SuperCollider setup frequently then doing more processing at that end makes sense as it reduces the need for the controller to know about the synth architecture. In my case, I'm writing a lot more controllers at the moment so I've put the more of the note generation logic at that end. This means I can swap controllers easily without having to fiddle with the SuperCollider code.

To assist with all that, the following OSC responder code offers a useful amount of flexibility. It takes an OSC message, interprets the first string as the name of a synth to be instantiated and then adds the subsequent pairs of strings and numbers as parameters to that synth. What you can't do easily with this method is subsequently control the created synths so make sure that they free themselves (doneAction:2 in an EnvGen for example) unless you want a huge pileup.

o = OSCresponder(nil, "/controller", {
    arg t, r, msg;
    var synthName, synthArgs;

    // first argument is the synth name
    synthName = msg[1].asSymbol;

    // remove the first two elements of OSC message, these contain 
    // the path and synth name
    1.do({|i| msg.removeAt(i)});

    // create a new array to hold the synth arguments
    synthArgs = Array.new(msg.size);

    // for the remaining elements of the OSC message go through and convert the first
    // of every pair to be a control name and add the second as the control data
    msg.do({
        arg item, i;
        if(i.even, {synthArgs.add(item.asSymbol)}, {synthArgs.add(item)});
    });

    // create the new synth
    Synth.new(synthName, synthArgs);
}).add;