Strange offset behaviour when plotting some .kr uGens

When I run this code:

{
    var impulse = Impulse.kr(0);

    var var1 = LFSaw.kr(300,1);
    var var2 = WhiteNoise.kr(1);

    var var3 = LFSaw.ar(300,1);
    var var4 = WhiteNoise.kr(0);

    [impulse, var1]
}.plot(0.01)

, different variables in the output array give different appearances to the impulse for no discernible reason. Choosing var1 or var2 keeps the impulse at 0s, whereas choosing var3 or var4 delays it by a single control sample.

I don’t know if it’s intentional, and any explanation would be greatly appreciated.

Very strange indeed. Same with SinOsc instead of Impulse BTW

{
    var impulse = SinOsc.kr(300);

    var var1 = LFSaw.kr(300,1);
    var var2 = WhiteNoise.kr(1);

    var var3 = LFSaw.ar(300,1);
    var var4 = WhiteNoise.kr(0);

    [impulse, var1]
}.plot(0.01)

In general the init behaviour of many UGens is erratic, but this kind of dependency is even more curious.

I guess it’s somehow due to the different rates of var3 and var4. If any of the plotted channels are audio rate, the rest is converted with K2A.ar. (WhiteNoise.kr(0) is in fact converted to audio rate by UGen.replaceZeroesWithSilence). K2A is applying a linear interpolation which i guess starts at zero. Don’t know if it could be considered a bug in K2A?

The example still behaves the same way if
var var4 = 0;
so I don’t think it could be just an audio rate problem.

Also worth nothing that
[impulse, var2, 0]
shows that WhiteNoise.kr(1) starts on time.

Yes, but in the .plot call, zeroes are converted to audio rate (replaced with Silence.ar). That’s what UGen.replaceZeroesWithSilence is doing, inside Function.asBuffer.

I guess it looks like that, but it still goes through the K2A interpolation. Compare:
{ K2A.ar(Impulse.kr(0)) }.plot(0.01)
with
{ K2A.ar(WhiteNoise.kr(1)) }.plot(0.01)

That’s my guess at least, although I don’t have the math skills to confirm that the interpolation is done right.

Not sure if this is a bug, but I know why this is happening (and perhaps more importantly how to avoid it). The problem is that a function automagically decides whether you want your output values are going to be audio rate, or control rate. So when you return var1/var2 with impulse it decides that this is a control rate bus. With var3/var4 it decides that this is an audio rate bus (and does the relevant interpolation). For var3 this makes as much sense as anything. For var4 this is probably a bug

As an illustration of what is going on.

({
    var impulse = Impulse.kr(300);

    var var1 = LFSaw.kr(300,1);
    var var2 = WhiteNoise.kr(1);

    var var3 = LFSaw.ar(300,1);
    var var4 = WhiteNoise.kr(0);

  [impulse, A2K.kr(var3)];
}.plot(0.01))

Compare to:

({
    var impulse = Impulse.kr(300);

    var var1 = LFSaw.kr(300,1);
    var var2 = WhiteNoise.kr(1);

    var var3 = LFSaw.ar(300,1);
    var var4 = WhiteNoise.kr(0);

  [K2A.ar(impulse), var3];
}.plot(0.01))

Weirdness like this is why I try to stay away from defining synths with functions, though with graphs it’s tricky. There’s no way I can think of to force [impulse,var4] to be a control rate array, so I guess this is a bug. The workaround is obviously not to declare a kr value after and ar value.

1 Like

Thanks for the explanation. What I want is an audio rate output, but for the impulse to be on sample 0 instead of sample 1.

On a slight side note, this bit of code is behaving very strangely too.

{
    var bufferA = Buffer.loadCollection(s, [4]);
    var bufferB = Buffer.loadCollection(s, [4]);
    var playA = PlayBuf.kr(1, bufferA);
    var playB = PlayBuf.kr(1, bufferB) + 1;
    RecordBuf.kr(-0.5 * playA, bufferA);
    RecordBuf.kr(-0.5 * playB, bufferB);
    [playA, playB, Silent.ar];
}.plot(0.01)

As far as I can tell, Buffer.loadCollection must allocate the buffer at the beginning of the control period as written, but only actually load the collection (the number 4) at the end of the control period.

I had the same problem, plot is not compatible with control rate ugens. This is a bug on my todo list !

1 Like

That’s not strictly true. Plot will work just fine with control rate ugens. The issue here is the way that SuperCollider builds the UGen when you pass a function.

I think if you have an array where one of the values is audio rate, then the control rate synth should be promoted to audio rate, which is what happens. None of the behavior there is wrong - this is exactly what you would expect when interpolation occurs. The problem is that for some reason when you define a control rate synthdef after an audio rate one and then put that in an array with another control rate UGen, SuperCollide decides to build a UGen array that is audio rate. The workaround is probably just to use audio rate ugens, rather than control rate.

As a general point, if you want sample rate accuracy always use audio rate.

Thanks for the explanation. What I want is an audio rate output, but for the impulse to be on sample 0 instead of sample 1.

Use:

Impulse.ar(300)

If you’re worried about sample accurate readings you should be using audio rate.

For your buffer example you’ve added Silent.ar to your buffer, so everything in it will be promoted to audio rate, so you’re dealing with interpolation.

For your buffer example - is there a reason you’re reading those at control rate? Not sure what you’re trying to do there, but if you change everything to ar it’s more likely to be successful. If you’re trying to do feedback, I don’t think that this approach is going to give you the results that you want. Look at SoundIn

I’m having trouble understanding exactly how the positioning and interpolation works. Is there a way to plot a function as a bar graph, or any format suitable for sample-by-sample troubleshooting (such as into a plain text file)? discrete isn’t a valid argument for plot (Function).

I definitely want sample accuracy at both control rate and audio rate though, for many reasons.
What I want is an audio rate output, but for the impulse to be on sample 0 instead of sample 1.
^ Sorry, that was very badly worded… what I meant is that I want the impulse to be on sample 0 in control rate, and then to convert that (with linear interpolation) to audio rate.
It seems that Trig1 covers this though:

(
{
	var impulse0 = Trig1.kr(LFPulse.kr(300), 0);
	var impulse1 = Impulse.kr(300);
	K2A.ar([impulse0, impulse1])
}.plot(0.01)
)

Thanks for the SoundIn suggestion! I suspect that it doesn’t match my use case because it seems to work with block sizes rather than single samples, unless I’ve misunderstood that. For control sample feedback I use buffers with complicated workarounds for the sample 0 problem, and for audio samples it looks like Fb1 is the best bet? (If I can ever make sense of it.)

I’ve got a workaround for this now.

s.quit;
Server.local.options.blockSize = 4;
s.boot;

{[
    LFTri.ar(s.sampleRate/2, 1, 2), // just to show where the samples are
    Impulse.ar(0),                  // audio rate impulse in correct place
    Impulse.kr(0),                  // control rate impulse with offset issue
    Trig1.kr(1, 0)                  // control rate impulse in correct place
]}.plot(70/s.sampleRate);

{[
    LFTri.ar(s.sampleRate/2, 1, 2),                                     // just to show where the samples are
    Phasor.ar(Impulse.ar(0), 1/8, -2, 2, -0.625),                       // Phasor as it behaves in audio rate
    Phasor.kr(Impulse.kr(0), 1/2, -2, 2, -0.625),                       // Phasor as it behaves in control rate
    Phasor.kr(Impulse.kr(0), 1/2, -2, 2, -0.5) + (1.5 * Trig1.kr(1, 0)) // Phasor in control rate patched to match audio rate
]}.plot(70/s.sampleRate)

I assume the problem is that the first control rate sample is meant to be more of a ‘sample -1’ than ‘sample 0’, hence why plot omits it when the output is at control rate. This would be fine if audio rate uGens started after one control period. Since they don’t, I’m happy to just adjust the code to make the subsequent samples correct, and then use ​ + (x * Trig1.kr(1, 0)) to patch the first sample. The only problem now is that it’s more complicated to do that with some other uGens.

I don’t really understand why you insist on using control rate signals. Audio rate will work and give you the right result.

Only to save processing and reduce xruns. Both audio rate and control rate with my patch will work fine, and patched control rate seems to be less processor-intensive than audio rate (I’m using a block size of 64 instead of the 4 in that example).

I assume the problem is that the first control rate sample is meant to be more of a ‘sample -1’ than ‘sample 0’, hence why plot omits it when the output is at control rate. This would be fine if audio rate uGens started after one control period. Since they don’t, I’m happy to just adjust the code to make the subsequent samples correct, and then use ​ + (x * Trig1.kr(1, 0)) to patch the first sample. The only problem now is that it’s more complicated to do that with some other uGens.

You assume wrong. The problem is that plot is not plotting these signals correctly. That’s it. You’re making erroneous assumptions based upon a faulty reading. You don’t need to adjust your code.

If you want to accurately plot control rate signals I would make them audio rate instead. Otherwise stop worrying about this - the samples are fine, your proposed ‘solution’ will make them wrong.

I just recorded the signals to Audacity using Out.ar and .play and got the same results given by plot. The only additional problem with plot is when the output array consists entirely of control rate signals. My solution does give sample accuracy.

I submitted this PR


Don’t know if it solves your sample accuracy problem though.

Again the issue is that you’re converting a control signal to an audio signal. Out.ar is converting the control signal to an audio signal. You’re not looking at the original control signal in audacity - you’re looking at a converted signal and there is a loss of accuracy with the conversion. The original signal was fine the problem was with the conversion.

The control signal will always be correctly aligned to the block. If you convert the control signal to audio there is no guarantee that it will remain aligned to the block. If you need a sample accurate audio signal then start with an audio ugen rather than a control one.

No your solution gives sample accuracy when you convert THIS control signal to audio. For other signals or situations it will not. It could make things worse. A control signal by definition is not sample accurate. If you truly need sample accuracy then use audio signals

@to7m - why you don’t you post a real example of a ugen where you’re worried about this - and then we can measure the audio output (i.e. the thing you really care about) to see if there’s really a problem, or if (as I suspect) you’re simply measuring an artefact of converting a control rate signal to an audio rate one.