Fixing AudioControl and InFeedback

Hello everyone!

I have noticed a weird behavior of the AudioControl and InFeedback UGens.

If you run this code, you’ll notice that the last audio cycle will run twice, generating a “double” dropout:

(
s.waitForBoot({
	SynthDef(\test, {
		Out.ar(\out.ir(0), { SinOsc.ar(2) * EnvGen.ar(Env([1, 1], [0.2]), doneAction:2) })
	}).add;

	s.sync;

	~bus = Bus.audio(s);

	Pbind(
		\instrument, \test,
		\dur, 1,
		\out, ~bus.index,
		\addAction, \addToHead
	).play;

	~reader = { \in.ar }.play(args:[ \in, 'a' ++ ~bus.index]);

	{ In.ar(0) }.plot(0.5);
})
)

This happens because the AudioControl UGen (as well as the InFeedback one) only check if the bus has been touched in the current audio cycle. In my opinion, it should probably take into consideration if the bus has been touched in the previous audio cycle aswell, in order to avoid this “repetition” effect.

I have compiled a custom UGen that does this, and it seems to me to handle the behavior better. It basically works just like In when the ~reader Synth is later in the chain, and just like InFeedback when the ~reader Synth is before in the chain (reading the previous audio cycle).

Pbind (\addToHead) -> ~reader:

-> ~reader Pbind (\addToTail) ->:

Would you think the current behavior of AudioControl and InFeedback is faulty as I do? Do you think it’s worth to make a PR to SuperCollider to fix this?

1 Like

Yes, would be great to fix this. I don’t see any reason to maintain backward compatibility for the doubled buffer.

hjh

Opened a PR:

1 Like

Is it possible to .plot when using InFeedback or is this issue also causing some implication on the .plot ?

This sound here:

(
SynthDef("help-InFeedback", { arg out=0, in=0;
    var input, sound;
        input = InFeedback.ar(in, 1);
        sound = SinOsc.ar(input * 1300 + 300, 0, 0.4);
        Out.ar(out, sound);

}).play;
)

does not seems to be properly plotted here:

(
{
    var input, sound;
        input = InFeedback.ar(0, 1);
        sound = SinOsc.ar(input * 1300 + 300, 0, 0.4);
        sound;
}.plot(1/10);
)

I strongly suspect plot diverts it from the actual bus since you can’t hear it. So you need consider what you In there and the huge mess that control namespaces are right now. Look at plot's implementation to see how it changes the out. It turns out that it doesn’t, just ignores it, meaning it writes nothing to it anymore.

The actual implementation, after going though some wrappers, is in Function.asBuffer. What that does is

			var	val = SynthDef.wrap(this);
/// .......
RecordBuf.perform(RecordBuf.methodSelectorForRate(rate), val, bufnum, loop: 0);

So nothing gets written to bus 0 anymore, in your example, or to any out for that matter. So you can’t use plot with feedback like that, unless you use a LocalIn and LocalOut.

You can probably write a version of plot and asBuffer that writes to a “private” bus automatically, to make that work. Left as an exercise to the reader, for now, esepecially because you’d need to know how out was declared by the user, without this kind of unifying patch.

1 Like

It’s probably just easier to use plotNRT for something like that, which will work because it uses a separate server to render, no SynthDef.wrap headaches.

(
~plotNRT.({
    var input, sound;
        input = InFeedback.ar(0, 1);
        sound = SinOsc.ar(input * 1300 + 300, 0, 0.4);
        sound;
}, 1/10);
)

Gets me this:

1 Like

Great, thanks! Maybe this .plotNRT should be incorporated on the main branch to allow these plotting possibilities + some doc improvement to alert users about that…