Back at the computer… some concrete examples.
I did find that my PR had one mistake. I had intended to “[add] descendantless units in forward order” but the first version of it doesn’t change reverseDo
to do
. I’ve fixed that now in the PR.
(
SynthDef(\outSideEffect, {
Out.ar(0, PinkNoise.ar() * 0.1);
ReplaceOut.ar(0, LPF.ar(In.ar(0, 1), 440));
}).dumpUGens;
)
[ 0_PinkNoise, audio, [ ] ]
[ 1_*, audio, [ 0_PinkNoise, 0.1 ] ]
[ 2_Out, audio, [ 0, 1_* ] ]
[ 3_In, audio, [ 0 ] ]
[ 4_LPF, audio, [ 3_In[0], 440 ] ]
[ 5_ReplaceOut, audio, [ 0, 4_LPF ] ]
^^ This example produces the expected, useful order in both the develop
and my PR branches (with the forward order fix) – “Won’t your patch reverse this?” No, it doesn’t (though maybe it did, at the time you asked).
I’ve seen it dozens of times where BufRd got pulled up earlier in the graph, before the writer…
(
SynthDef(\notOK, { |out, bufnum|
var ph = Phasor.ar(0, 1, 0, BufFrames.kr(bufnum));
var sig = SinOsc.ar(440);
var writer = BufWr.ar(sig, bufnum, ph);
var reader = BufRd.ar(1, bufnum, ph);
Out.ar(out, reader);
}).dumpUGens;
)
notOK // this is the current dev-branch ordering
[ 0_Control, control, nil ]
[ 1_BufFrames, control, [ 0_Control[1] ] ]
[ 2_Phasor, audio, [ 0, 1, 0, 1_BufFrames, 0.0 ] ]
[ 3_BufRd, audio, [ 0_Control[1], 2_Phasor, 1.0, 2 ] ]
[ 4_Out, audio, [ 0_Control[0], 3_BufRd[0] ] ]
[ 5_SinOsc, audio, [ 440, 0.0 ] ]
[ 6_BufWr, audio, [ 0_Control[1], 2_Phasor, 1.0, 5_SinOsc ] ] <<-- NO! This is not what you want
Before testing these defs, I had thought that maybe moving the Phasor to immediately precede BufWr might fix the order – but this didn’t pan out.
Curiously, your LocalBuf example does produce the right order, but with a bit of testing, it turns out that this is only because your Phasor
has no antecedents. If nf = BufFrames.kr(...)
, then the order is broken! But basing a Phasor on BufFrames is an entirely normal thing to do – so I’d say that your example is an unusual case, and that in reality, considering all the cases we want to support, it’s brittle and quite unreliable to assume that BufWr will come first just because it was written first.
In current SC, it is always safer to force the order using firstArg
.
(
SynthDef(\ok, { |out, bufnum|
var ph = Phasor.ar(0, 1, 0, BufFrames.kr(bufnum));
var sig = SinOsc.ar(440);
var writer = BufWr.ar(sig, bufnum, ph);
var reader = BufRd.ar(1, bufnum <! writer, ph);
Out.ar(out, reader);
}).dumpUGens;
)
ok
[ 0_SinOsc, audio, [ 440, 0.0 ] ]
[ 1_Control, control, nil ]
[ 2_BufFrames, control, [ 1_Control[1] ] ]
[ 3_Phasor, audio, [ 0, 1, 0, 2_BufFrames, 0.0 ] ]
[ 4_BufWr, audio, [ 1_Control[1], 3_Phasor, 1.0, 0_SinOsc ] ]
[ 5_firstArg, audio, [ 1_Control[1], 4_BufWr ] ]
[ 6_BufRd, audio, [ 5_firstArg, 3_Phasor, 1.0, 2 ] ]
[ 7_Out, audio, [ 1_Control[0], 6_BufRd[0] ] ]
BUT… with my patch… let’s go back to the notOK version… suddenly it’s become OK.
[ 0_SinOsc, audio, [ 440, 0.0 ] ]
[ 1_Control, control, nil ]
[ 2_BufFrames, control, [ 1_Control[1] ] ]
[ 3_Phasor, audio, [ 0, 1, 0, 2_BufFrames, 0.0 ] ]
[ 4_BufWr, audio, [ 1_Control[1], 3_Phasor, 1.0, 0_SinOsc ] ] <<-- YES! This is the right way
[ 5_BufRd, audio, [ 1_Control[1], 3_Phasor, 1.0, 2 ] ]
[ 6_Out, audio, [ 1_Control[0], 5_BufRd[0] ] ]
… because Out pulls its immediate ancestor (BufRd) into the tail before getting to the other BufWr branch.
So I still think the PR is onto something (though more testing is needed).
hjh