Possible bug with In.ar and NumOutputBuses ugen

This may just be my misunderstanding something fundamental, but I fell like this should work.

The following code compiles with no problems:

SynthDef(\safety, {|in|
	ReplaceOut.ar(0, In.ar(in, 2))});

But this slightly more portable code throws an error:

SynthDef(\safety, {|in|
	ReplaceOut.ar(0, In.ar(in, 2))});

with the following gorgeous error:

ERROR: Non Boolean in test.
RECEIVER:
Instance of BinaryOpUGen {    (0x1780a0cb8, gc=C8, fmt=00, flg=00, set=04)
  instance variables [9]
    synthDef : instance of SynthDef (0x131a86ec8, size=16, set=4)
    inputs : instance of Array (0x13271f6a8, size=2, set=2)
    rate : Symbol 'scalar'
    synthIndex : Integer 3
    specialIndex : Integer 8
    antecedents : nil
    descendants : nil
    widthFirstAntecedents : nil
    operator : Symbol '<'
}

PROTECTED CALL STACK:
	Meta_MethodError:new	0x130443080
		arg this = MustBeBooleanError
		arg what = nil
		arg receiver = a BinaryOpUGen
	Object:mustBeBoolean	0x120125140
		arg this = a BinaryOpUGen
	MultiOutUGen:initOutputs	0x130b51f00
		arg this = an In
		arg numChannels = a NumOutputBuses
		arg rate = audio
	a FunctionDef	0x1506201a8
		sourceCode = "{|in|
	ReplaceOut.ar(0, In.ar(in, NumOutputBuses.ir()))}"
		arg in = an OutputProxy
	SynthDef:buildUgenGraph	0x1318b0580
		arg this = a SynthDef
		arg func = a Function
		arg rates = nil
		arg prependArgs = [  ]
		var result = nil
		var saveControlNames = nil
	a FunctionDef	0x1318aeb00
		sourceCode = "<an open Function>"
	Function:prTry	0x130747680
		arg this = a Function
		var result = nil
		var thread = a Thread
		var next = nil
		var wasInProtectedFunc = false
	
CALL STACK:
	MethodError:reportError
		arg this = <instance of MustBeBooleanError>
	Nil:handleError
		arg this = nil
		arg error = <instance of MustBeBooleanError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of MustBeBooleanError>
	Object:throw
		arg this = <instance of MustBeBooleanError>
	Function:protect
		arg this = <instance of Function>
		arg handler = <instance of Function>
		var result = <instance of MustBeBooleanError>
	SynthDef:build
		arg this = <instance of SynthDef>
		arg ugenGraphFunc = <instance of Function>
		arg rates = nil
		arg prependArgs = nil
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "SynthDef(\safety, {|in|
	Rep..."
		var doc = nil
		var ideClass = <instance of Meta_ScIDE>
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ The preceding error dump is for ERROR: Non Boolean in test.
RECEIVER: a BinaryOpUGen

Is this a bug that I should raise on the github, or have I just misunderstood something?

As far as I can tell this is something about the second input for In.ar where it only accepts values.

Think you copy and pasted your examples wrong?

Did you mean this?

NumberOutputBuses won’t work because it is a ugen whose value is set (I believe) when each synth instance is created on the server (which is ir), not when the synthdef is compiled in the language, and this usage would change the number of channels (requiring rebuilding the synthdef).

Does s.options.numOutputBusChannels do the same thing?

Ah yes I did. Sorry.

I wondered if that was the case, it’s just that the In.ar documentation implies that you can set the number of channels at ‘synth’ time, rather than ‘synthdef’ time. But given what I know of the server architecture that does seem rather unlikely…

Yes I have a workaround for it - just wanted to check it was appropriate behavior. Thanks.

Can you point to that, because that definitely should be changed.

I don’t see anything suggesting this… It does say for numChannels “You cannot modulate this number by assigning it to an argument in a SynthDef” and I suppose this is a bit unclear – it should say “you cannot modulate this number by any means, ever, under any circumstance.”

I suppose SC4 should find a way to handle dynamic UGen graphs – because this is a case where the rule is absolute and unequivocal (nothing is ever permitted that would alter the structure of a SynthDef at Synth time) and we as a community struggle to accept the absolute-ness of the rule (i.e. always looking for loopholes, “maybe this one time it will work”). This means the demand for dynamic SynthDefs is high – but the technical challenges are also extremely difficult, so it won’t happen quickly.

hjh

I don’t see anything suggesting this… It does say for numChannels “You cannot modulate this number by assigning it to an argument in a SynthDef” and I suppose this is a bit unclear – it should say “you cannot modulate this number by any means, ever, under any circumstance.”

Well the word ‘modulate’ doesn’t rule out setting it as an IR value. There are ugens where the values can only be set when the ugen is initiated. Obviously that wouldn’t work because of how synthdefs are constructed. As an experienced user I should have realized that (blame a lack of sleep), but I think it’s a reasonable interpretation.

I think you will now get this error ‘Must be a nonzero, positive integer. This is fixed when the SynthDef is compiled so cannot be assigned to a SynthDef argument.’ per this PR class library: MultiOutUGen: better error for non-numeric numChannels arg by sirlensalot · Pull Request #6190 · supercollider/supercollider · GitHub