This works but loses the play
functionality (semantics) in NodeProxy this way, i.e. one would have to rely on pause
and resume
.
A more elaborate way to do this that preserves play
functionality is to define your own custom Monitor
. The default one is almost flexible because its playNToBundle
method does take in a defName
, but this only happens at the last level in the call hierachy and ther are like 5 nested calls before reaching that one so one would have to replicate a lot Monitor machinery in user code to leverage that directly. The more flexible approach is to make defName
an instance variable (in a subclass) like so
FlexMonitor : Monitor {
var <>defName = "system_link_audio_1";
copy { // not tested
^this.class.newCopyArgs(*[ins, outs, amps, fadeTimes, vol, defaults, defName].deepCopy)
}
playNToBundle {
| bundle, argOuts, argAmps, argIns, argVol, argFadeTime, inGroup, addAction,
defName, multi = false |
^super.playNToBundle(
bundle, argOuts, argAmps, argIns, argVol, argFadeTime, inGroup, addAction,
defName ? this.defName, multi)
}
}
The one can make a modified copy of the system_link_audio_1
synthdef (from SystemSynthDefs) to do ReplaceOut
instead of Out
.
(
var i = 1; // only 1-chan version is ever used by JITlib
SynthDef("system_replace_audio_" ++ i,
{ arg out=0, in=16, vol=1, level=1, lag=0.05, doneAction=2;
var env = EnvGate(i_level: 0, doneAction:doneAction, curve:'sin')
* Lag.kr(vol * level, lag);
ReplaceOut.ar(out, InFeedback.ar(in, i) * env)
}, [\kr, \kr, \kr, \kr, \kr, \ir]).add;
)
And now for tests of the above:
Ndef.clear;
a = Ndef(\ah, { 0.5 * SinOsc.ar(777) !2 }).play;
b = Ndef(\bbh, { 0.5 * SinOsc.ar(333) !2 }).play;
s.queryAllNodes; // shows use of the default system_link_audio_1
s.scope; // normal Monitor play mixes
a.stop; b.stop;
// won't have effect to change monitor type while playing
a.monitor = FlexMonitor.new.defName_("system_replace_audio_1");
b.monitor = FlexMonitor.new.defName_("system_replace_audio_1");
a.play; b.play; // only one heard (should be b)
s.queryAllNodes; // should see system_replace_audio_1
b.stop; // a heard now
// with explicit order control:
a.play; b.play(addAction: \addToHead) // a heard because the order is b then a.
a.stop; // b heard
I’m guessing few people needed this because the default Monitor is a bit clumsy to use flexibly like this.
There are also a bunch of additional synth defs in SystemSynthDefs, but none of these seem used by anything: system_link_control
is not used unlike _audio
variant; and likewise system_setbus_hold_audio
and system_setbus_audio
. Only
system_setbus_control
is used by something, but that’s in the test suite, by TestEvent.
I suppose the only time when this is default monitor synth replacement useful is when one mixes NodeProxies with synths generated in some other way, either directly or through some other framework or library.
Otherwise, any NodeProxy essentially acts as a bus mixer on its array of sources. The more obscure feature of the \filter
NodeProxy role is that it replaces output by default.
a = Ndef(\ah, { 0.5 * SinOsc.ar(777) !2 }).play
a[1] = { 0.5 * SinOsc.ar(333) !2 } // mixes with a[0] def above
a[1] = nil // removes
a[1] = \filter -> { 0.5 * SinOsc.ar(333) !2 }
// ^^ replaces output, because \wet1 is 1 by default
s.queryAllNodes(true) // can see \wet1 is 1
a.set(\wet1, 0.5) // now it's a mix
a.set(\wet1, 0) // and now passthrough
The only slight gotcha with this is that the syntax is a bit non-obvious when mapping other proxies this way:
Ndef.clear
a = Ndef(\ah, { 0.5 * SinOsc.ar(777) !2 }).play
b = Ndef(\bbh, { 0.5 * SinOsc.ar(333) !2 })
a[1] = \filter -> b // won't work like this
a[1] = \filter -> { b.ar(2) } // ok