Why does multichannel expansion sometimes not work for `mul` in NodeProxy?

Hi there, I am puzzled as to why these work (both creating a 2-channel NodeProxy):

Ndef(\test, { SinOsc.ar([440,442], 0, 0.2) })
Ndef(\test, { SinOsc.ar([440,442], [0, 0], 0.2) })

But if you put an array in the mul argument, it doesn’t work:

Ndef(\test, { SinOsc.ar([440,442], 0, [0.2, 0]) })
// ^^ The preceding error dump is for ERROR: Out  input at index  2 ( 0.0 ) is not audio rate

It does work if you explicitly set a UGen array:

Ndef(\test, { SinOsc.ar([440,442], 0, DC.ar([0.2, 0])) })

It also works fine if you don’t use NodeProxy and just call play on the function (i.e. creating a SynthDef).

I guess it must be related to the face that the following returns a non-UGen…?

SinOsc.ar([440,442], 0, [0.2, 0])
// -> [ a BinaryOpUGen, 0.0 ]

Hmm, there must be some special case, because it also works if both mul array members are not zero:

Ndef(\test, { SinOsc.ar([440,442], 0, [0.2, 0.1]) })
// works fine replacing mul [0.2, 0] with [0.2, 0.1]

This caught me in a live-coding session; I couldn’t figure out why my Ndef (which included 0 in some channels for the amplitude) wouldn’t parse – luckily I thought to wrap it with DC.ar. But now, after debugging a bit, it seems it must be caused by some optimization?

That’s correct. Look in BinaryOpUGen and you’ll find it.

anySignal * 0 will always be 0. So there is no need to calculate it, and it’s simply replaced by a constant 0.

The title of the thread doesn’t quite accurately describe the issue, then: multichannel expansion for mul is fine, and there’s nothing special about NodeProxy here either. The issue is the “degenerate case” of times-zero.

hjh

The issue is the “degenerate case” of times-zero.

Thanks. But do you mean…it’s “not a bug”? It seems somewhat related to mul, at least in the sense that you don’t see this error if you put [0.2, 0] in the phase or freq arguments, nor if you multiply those by zero.

Also, it works fine if you directly create a SynthDef (insteada of assigning as a NodeProxy source) doing this:

{ SinOsc.ar([440,442], 0, [0.2, 0]) }.play

You’re right the title isn’t great, then… How should I describe the issue better?

I guess the bug would be that NodeProxy should or could automatically promote channels to the highest rate in the array (but currently doesn’t).

There are two viewpoints on things like this: 1/ if you intended to supply an array of signals but one element isn’t a signal (or more), then this is a failure case and you should be warned about that; or 2/ SC should try to do something sensible with the given data, even if it isn’t formally correct. The current behavior here follows 1/ but you were expecting 2/. My personal opinion is that 2/ would be more compatible with NodeProxy and there’s probably no reason not to change it to be more forgiving.

But there could be some use case where the error would be better. For instance, a constant 0 would do no damage. A constant 5 would DC-offset that channel outside normal full scale so it might be better to prevent that (or pass it through and advise users to add a DC-blocker to any live coding setup).

hjh

I agree, it would be more natural to just interpret it and assume it was intentional.

Here is a fix:

Your example will post:

Ndef('test'): adopted proxy output signal at index 1 to audio rate:
0.0

Thanks @jamshark70 for the comments, and thanks @julian for the quick fix to this specific issue!