Although I’ve stopped using PmonoArtic for more complicated reasons, this issue is still quite befuddling to me. Pdef with crossfade (non-nil fadeTime
) does this:
Ppar([
EmbedOnce(
PfadeOut(stream, fadeTime, delta, tolerance),
cleanup
),
PfadeIn(newStream, fadeTime, delta, tolerance)
]).asStream
But amusingly, the bug doesn’t seem to be in that code:
x = PmonoArtic(\default, \dur, 0.2, \freq, Pwhite(9,16) * 100, \legato, 0.2, \amp, 0.05);
y = PmonoArtic(\default, \dur, 0.2, \freq, Pwhite(1,5) * 100 , \legato, 1, \amp, 0.2);
p = Ppar([x, y]).play // ok
p = Ppar([PfadeOut(x, 5), y]).play // ok
p = Ppar([x, PfadeIn(y, 5)]).play // ok too
p = Ppar([PfadeOut(x, 5), PfadeIn(y, 5)]).play // ok in combo
// even this works!
p = Ppar([EmbedOnce(PfadeOut(x, 5), EventStreamCleanup.new), PfadeIn(y, 5)]).play
At least that gives a workaround to using Pdef’s own crossfade.
I’m not sure what EmbedOnce is for, by the way. It actually seems to prevent some such failures, at a least on stop
p = Ppar([PfadeOut(y, 5), PfadeIn(x, 5)]).play // ok
p.stop // prints some failure if called after y fully fades
p = Ppar([EmbedOnce(PfadeOut(y, 5), EventStreamCleanup.new), PfadeIn(x, 5)]).play
p.stop // seems to prevent that failure
Also, an attempt at a workaround “a Pdef layer below” didn’t quite work as expected
v = Pbind(\dur, 0.2, \freq, Pwhite(9,16) * 100, \legato, 0.2, \amp, 0.05)
w = Pbind(\dur, 0.2, \freq, Pwhite(2,4) * 100 , \legato, 1, \amp, 0.2)
Pdef(\two, PmonoArtic(\default) <> Pdef(\data))
Pdef(\data).fadeTime_(5)
Pdef(\data, v)
Pdef(\two).play
Pdef(\data, w) // v fadeout but not w fade in...
I suspect the actual problem is that EventPatternProxy
reuses its cleanup
object when you change its source
(does not re-init the cleanup), so it might be contaminated with stuff from previously set sources. I’ve (finally) managed to repro it by doing what Pdef is doing internally on a source swap:
(p = Prout({ |inval|
var cleanup = EventStreamCleanup.new, stream = x.asStream, cnt = 15, outval;
loop {
cnt = cnt-1;
if (cnt == 0) {
"source swap".postln;
stream = Ppar([EmbedOnce(PfadeOut(x, 5), cleanup), PfadeIn(y, 5)]).asStream;
};
outval = stream.next(inval);
outval = cleanup.update(outval);
inval = outval.yield;
};
}).play)
I doesn’t bomb immediately on source swap though, but after the fading in Ppar finishes.
The cleanup system in SC quite buggy, so the actual problem may be elsewhere. For now, it’s best to avoid patterns that use cleanup, even internally (like Pmono series).
Actually what happens is that cleanup for the stuff being cross-faded in gets called as soon as the cross-fade is done. The next code doesn’t stop on cross fade, but cleanup gets called (which in this case is harmless, unlike for Pmono or PmonoArtic with legato >= 1)
Somewhat simpler code to show where the problem is, namely the cleanup gets called prematurely:
Pdef(\two).fadeTime_(2);
Pdef(\two, PmonoArtic(\default, \dur, 0.2, \freq, Pwhite(2,4) * 100 , \legato, 1)).play
// no stops with the next, but the cleanup gets called!
Pdef(\two, Pfset({}, Pbind(\dur, 0.2, \freq, 440), { "bye".postln })).play
Filed as a bug: