20.do{} vs. foo.do{}

Well, the exact solution depends on how one wants these number-of-layers changes to occur. If it’s ok to tear up all the previous layers as in: this note has 5 layers, the next note has 10, a simple wrapper like that is enough. For this particular example of additive synthesis, strummed events work just as well:

i = { VarSaw.ar(\freq.kr(100) * Rand(0.98, 1.02 ! 2), Rand(0.0, 1.0 ! 2), 0.5, 0.08) }
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2 ! 5, freq: 140).play
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2 ! 10, freq: 200).play
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2 ! 40, freq: 240).play

f = { rrand (100, 300) }
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2, freq: f ! 5).play
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2, freq: f ! 10).play
(instrument: { EnvGate(fadeTime: 1) * i.() }, dur: 2, freq: f ! 20).play

If one wants to change the polyphony “level” as notes/nodes are playing, without tearing up the the ones already playing, it gets a bit more complicated. Adding new layers is fairly trivial by piling on more events/nodes, but removing some means tracking the ids/nodes and releasing them.

(
fork { 
	e = (type: \on, instrument: { EnvGate(fadeTime: 0.2) * i.() }, freq: f ! 12).play;
	e[\id].do { |x| 0.75.wait; Node.basicNew(s, x).release } 
}
)

(Instead of Node.basicNew...release you can also write (type: \off, id: x).play here.)

But in general, if the do iteration doesn’t have an equivalent in server commands (like it did here for additive synthesis), and you still want a dynamic knob for it, there’s no workaround for writing your own UGen, which alas means going “out of SC language” to C++, Faust, or even Nim.

1 Like