Monday, July 9, 2012

Load Balancing your Audio Rendering on a Multi-Core Processor

This block of code will define 4 servers to render audio, and assign the list of them to the variable ~z to implement round-robin load-balancing of the audio rendering SuperCollider isn't multicore-aware when rendering audio (at least in version 3.4) so to split the rendering to multiple cores, you have to assign 1 core per server (and then presumably OSX will assign each of these scsynth processes to its own core - check your Activity Monitor to see).


//instantiate 4 servers for a 4 core machine
Server.internal.boot;
Server.local.boot; 
~x = Server.new("two",NetAddr("127.0.0.1", 9989));
~x.boot; ~x.makeWindow;
~xx = Server.new("three",NetAddr("127.0.0.1", 9988));
~xx.boot; ~xx.makeWindow;

//you could also say Server.all here instead of the list of servers.
~z = [Server.internal, Server.local,~x,~xx];

//a placeholder synth
~z.collect({|z|SynthDef("my-synth",{nil}).send(z)})

//here's a simple player routine 
~r = Routine {
  inf.do({|x|
      //instantiate a synth, and set it to a random Server in ~z
      var sn = Synth("my-synth",nil,~z.choose);
      //set some arguments on the synth
      sn.set(\foo,x);
      1.wait;
  })
};
~r.play;
/*
a different example - hit each server sequentially.
this may not be balanced if each synth you instantiate takes different server resources
for example, if every other beat plays a long note, then the long notes will pile up on half of the servers.
*/
~r = Routine {
  inf.do({|x|
      //assign synths sequentially for each step on the routine
      var sn = Synth("my-synth",nil,~z.wrapAt(x));
      //set some arguments on the synth
      sn.set(\foo,x);
      1.wait;
  })
};
~r.play;

/*
keep hitting the same server until it starts to max out, then move down the list to the next server
*/
~r = Routine {
  inf.do({|x|
    var playing = false;
      ~z.size.do({|n|
        //signal starts to break up when peak cpu gets above 80 - but this can depend on your hardware
        /* 1000 Synths is the default max per Server - polling doesn't happen instantaeously so set this a little low */
        (~z.wrapAt(n).peakCPU < 80).and(~z.wrapAt(n).numSynths < 900).and(playing==false).if({
          //add our synth to nth Server in the list
          var sn = Synth("my-synth",nil,~z.wrapAt(n));
          //set some arguments on the synth
          sn.set(\foo,x);
          //break the loop
          playing=true;
        });
      });
      x.postln;
      //if all the Servers in ~z are over 80% capacity, the music carries on without playing the note 
      (0.1).wait;
  })
};
~r.play;

No comments:

Post a Comment