Weird error ("Non Boolean in test") with InFeedback constructor

The following gives a really weird “ERROR: Non Boolean in test” when parameterizing numChannels in InFeedback (full trace at end):

(
add(SynthDef(\weird, { arg in, out, numChannels=2;
	var a = InFeedback.ar(in,numChannels);
	Out.ar(out,a);
	Out.ar(in,a);
}));
)

Eliding numChannels arg and hardcoding to 2 has no issue.

Stack trace:

RROR: Non Boolean in test.
RECEIVER:
Instance of BinaryOpUGen {    (0x2801f7aa8, gc=60, fmt=00, flg=00, set=04)
  instance variables [9]
    synthDef : instance of SynthDef (0x159e045d8, size=16, set=4)
    inputs : instance of Array (0x130e0e5b8, size=2, set=2)
    rate : Symbol 'control'
    synthIndex : Integer 2
    specialIndex : Integer 8
    antecedents : nil
    descendants : nil
    widthFirstAntecedents : nil
    operator : Symbol '<'
}
PATH: .../2024JanWorkspace.scd

PROTECTED CALL STACK:
	Meta_MethodError:new	0x15847ffc0
		arg this = MustBeBooleanError
		arg what = nil
		arg receiver = a BinaryOpUGen
	Object:mustBeBoolean	0x1480dcc00
		arg this = a BinaryOpUGen
	MultiOutUGen:initOutputs	0x158a62540
		arg this = an InFeedback
		arg numChannels = an OutputProxy
		arg rate = audio
	a FunctionDef	0x1483187b8
		sourceCode = "{ arg in, out, numChannels=2;
	var a = InFeedback.ar(in,numChannels);
	Out.ar(out,a);
	Out.ar(in,a);
}"
		arg in = an OutputProxy
		arg out = an OutputProxy
		arg numChannels = an OutputProxy
		var a = nil
	SynthDef:buildUgenGraph	0x1594e5380
		arg this = a SynthDef
		arg func = a Function
		arg rates = nil
		arg prependArgs = [  ]
		var result = nil
		var saveControlNames = nil
	a FunctionDef	0x1594e3900
		sourceCode = "<an open Function>"
	Function:prTry	0x15875f880
		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
	< closed FunctionDef >  (no arguments or variables)
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "(
add(SynthDef(\weird, { arg..."
		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

https://doc.sccode.org/Classes/InFeedback.html says:

“numChannels: The number of channels (i.e. adjacent buses) to read in. The default is 1. You cannot modulate this number by assigning it to an argument in a SynthDef.”

1 Like

Thanks, missed that in the docs.

It would be a nice improvement to have a better error message … I’ll poke around to see if that’s easy to do.

Yeh, this comes up all the time, you can add things like the below to a file called sanity.sc in your extensions folder!

+InFeedback {

	init { arg numChannels ... argBus;
		numChannels.isNumber.if {
			inputs = argBus.asArray;
			^this.initOutputs(numChannels, rate)
		} {
			"InFeedback: numChannels not a number".error
		}
	}

}

Yeh, this comes up all the time

Hmm, why wouldn’t that get validated in the source init then? Is there a performance hit doing the kind of minimal arg validation like you provided?

Well, more like the below I guess, &etc.

+Object {

	assertChannelCountIsANumber {
		this.isNumber.not.if {
			Error("assertChannelCountIsANumber: numChannels not a number?").throw
		}
	}

}

+In {

	init { arg numChannels ... argBus;
		inputs = argBus.asArray;
		^this.initOutputs(numChannels.assertChannelCountIsANumber, rate)
	}

}

+InFeedback {

	init { arg numChannels ... argBus;
		^this.initOutputs(numChannels.assertChannelCountIsANumber, rate)
	}

}

No reason I imagine, just no one’s done it? You could make a patch and send it in!

2 Likes

Here, no. This is at the time of building the SynthDef, not evaluating it in the server.

In this case, it looks pretty straightforward to check it (and more useful because the error message, as you noted, is misleading). There are a lot of other places where similar things happen (e.g. anArray[aUGen]) where there would be a performance hit in every array access if it were checked. But In or InFeedback.ar are much less frequently called.

This general principle – that a SynthDef’s structure must be fixed in stone, including channel counts, at SynthDef compile time – also happens to be one where a lot of users expect to be able to make an exception, but there is none. Probably one reason is that SynthDef structure is “once-removed” in SC (more abstract, or harder to visualize, than in graphical patchers), so it isn’t immediately obvious just from looking at the code exactly what is trying to create a dynamic synth structure (and as noted, it’s easy to overlook in the documentation).

hjh

PR here: class library: InFeedback: better error for non-numeric numChannels arg by sirlensalot · Pull Request #6190 · supercollider/supercollider · GitHub

1 Like