Dynamically fill arrays of signals inside a SynthDef

I would like to have a SynthDef made of different signals according to an external parameter called voices. When voices increases, I would like the synth to create the sig from an array of independent signals (eventually with different detuning according to their index inside the array).

Till now I’ve come up with this

SynthDef(\bassline4, {
	out=0, freq=440, pan=0.0, amp=1.0, 
	atk=0.05, rls=0.2, sub=0.0, subwidth=0.5,
	sync=0.0, voices = 1, detune=0.0, spread=0.0
	var env, sig;
	env = EnvGen.ar(Env.perc(atk, rls), 1, doneAction:2);
	sig = Array.fill(voices, {
		SyncSaw.ar(syncFreq:freq*(sync.midiratio), sawFreq:freq) + Pulse.ar(freq*0.5, subwidth, mul:sub);
	sig = sig * env * amp;
	sig = LeakDC.ar(sig);
	Out.ar(out, Pan2.ar(Splay.ar(sig, spread), pan));

but It doesn’t compile. Think the problem is the method Array.fill wants to know voices in advance.

Any idea on how can I obtain the desired result?
Thank you very much for your help

SynthDef's are, by their nature, fixed - this is mostly for performance and simplicity reasons. So, you can’t fill your array based on the Synth argument voices, because this would mean you’d be adding new things to your SynthDef while it’s running. In general, the smallest unit you can dynamically start or stop on the server is a Synth, not individual parts of a Synth.

If you’d like to keep things confined to a single SynthDef, and your synth is going to be relatively simple like the one you have above, then you can just create the max number of voices you’ll need ahead of time, and then use the voices argument to unmute them:

sig = Array.fill(32, { 
     amp = (voices > index);  // the ">" here will return 1 or 0 depending on voices
     amp * SyncSaw.ar(....);    

The resources for even 32 SyncSaws and Pulses is tiny - this should be perfectly fine unless you’re on a raspberry pi or you want 50 of these 32-voice synths running at once

The other option is to run a separate Synth per voice, and add or free these synths as you want more or fewer voices. You’d need to do something like:

  1. Split the SyncSaw.ar() + Pulse.ar() out to a separate (very simple) SynthDef.
  2. Allocate a bus and have all of the Synths from #1 write to that bus.
  3. Rather than the current sig = .... in your SynthDef, just read from the bus in #2.
  4. Make sure your “voices” Synths are before the Synth that’s reading them (use addActions and/or Groups).

Good luck!

Thank you @scztt for your detailed answer!