In theory, yes.
The current design concentrates tree/path-style access to args in Syn (Syn has argAtPath
; Plug does not). This is good for performance – the arg-path dictionary lives in only one location, and (after paths are cached), set
by path is practically constant-time. (First set
will be slower because it populates the subtree at this time; after that, it’s cached.) If this were distributed across Plugs (which would be conceptually better), access time might depend on the depth of the tree.
I was thinking in terms of a more-or-less drop-in replacement for Synth – if you use Syn without any Plugs, it should be basically the same as Synth, ideally not adding (much of) a performance hit. So the head of the tree is the focal point. I would have to think about what it would mean for the design if the focal point were, erm, less focal.
It’s not really meant to be an “everything” patching solution. My primary use case is polyphonic synths where synth inputs can be supplied by simple values or Plug signals. Ndefs are meant to stay around for a long time, and you can play with them freely, but they are terrible at polyphony. Syns are meant to play for a relatively short time and get released. A more purely recursive design would be more beautiful, but might also distract from the primary use case.
The server presently disallows this.
(
a = { |array3 = #[1, 2, 3], fourth = 4|
(array3 ++ [fourth]).poll(1);
Silent.ar(1)
}.play;
)
UGen Array [0]: 1
UGen Array [1]: 2
UGen Array [2]: 3
UGen Array [3]: 4
a.set(\array3, [100, 200]); // not enough: only first 2 change
UGen Array [0]: 100
UGen Array [1]: 200
UGen Array [2]: 3
UGen Array [3]: 4
a.set(\array3, [10, 20, 30, 40, 50]); // too many: only the 3 change
UGen Array [0]: 10
UGen Array [1]: 20
UGen Array [2]: 30
UGen Array [3]: 4
a.free;
Not currently. Server messaging wouldn’t be difficult, because mapping a multichannel control to buses already requires an array such as [\c1, \c2, \c3]. So channel mapping would simply be pre-ordering this array.
If you accidentally map a kr input to an ar bus, I think it just naïvely decimates down to control rate.
The other way is probably dangerous – good issue to log
Audio sources can crossfade:
(
a = Syn({ |ffreq = 2000|
LPF.ar(\in.ar(0!2), ffreq) * 0.1
}, [in: Plug({ Saw.ar([440, 441]) }, [fadeTime: 5], rate: \audio)]);
)
a.argAtPath("in").source = { VarSaw.ar([220, 221], 0, 0.7) };
a.free;
But that’s not quite enough – I’ll have to look at that later, in the code.
No, but I guess that isn’t really any different from Jordan’s question about deleting Syn and generalizing Plug.
Does it add to latency as jitlib?
If you mean InFeedback block delay, Syn/Plug try to keep the nodes in the right order so that there should be no additional latency in feed-forward graphs.
If you mean server messaging latency, /s_new messages should be performed on time (unless you have such a complex Syn, with a large number of ad hoc-function Plugs, that it blows away s.latency
).
I think a nice design would be to evaluate as many SynthDefs early (with some kind of macro, to produce variations of the functions); but also allow low latency and composability
Hm, the job of anticipating SynthDef variants that the user didn’t specify seems unrelated to automated bus mapping. I think that should be a separate quark.
Of latency, one absolute requirement of mine is that event type \syn
should sound on time, even when ad hoc SynthDefs need to be made from functions.
The big problem with composability is argument names. Syn’s path-style addressing is at least unambiguous, but requires intimate knowledge of the Syn’s structure (which discourages large and complex trees). At this time, I don’t have a better solution. (Crucial library seemed to be looking for general composability, but I never figured out the arg name problem in it.)
hjh