I'm surprised this doesn't work. I'm able to achieve multichannel expansion with vector literals. Outside of a SynthDef context, I'm also able to achieve multichannel expansion using both repeat & repeatedly:
(sin-osc (repeat 2 220))
=> (#<sc-ugen: sin-osc:ar [0]> #<sc-ugen: sin-osc:ar [0]>)
(sin-osc (repeatedly 2 rand))
=> (#<sc-ugen: sin-osc:ar [0]> #<sc-ugen: sin-osc:ar [0]>)
The problem being it's unable to use a SynthDef argument (n) with repeat/repeatedly.
(defsynth voices [n 8] (out 0 (sin-osc (repeat n 220))))
But, is dynamic multichannel expansion possible in the context of a SynthDef?I don't think that's possible in a dynamic way. In SuperCollider, when you define a synth, it actually compiles your synth graph down to a bytecode spec (defined here http://doc.sccode.org/Reference/Synth-Definition-File-Format.html) So before that definition gets compiled, the exact UGens that are going into it need to be determined. Multichannel expansion works by creating multiple UGens or multiple Synth instances. But if the number of UGens isn't known when you're defining the synth, there would be no way to determine what to put inside it
I'm not very familiar with Overtone, but there might be some abstract way of dealing with this. In the SuperCollider language there's a library called Instr that you can use to define "instruments", which is a nifty abstraction that handles compiling multiple synth defs for you as needed.
There might be something similar in Overtone - I'm curious what definst
does