Some misunderstandings with classes

I’ve got some issues when designing a new class.
Issue (1): declaring const, var, classvar is limited to only a few type of primitives/classes.
E.g. const aConst = [1,2,3]; throws a Build-time error

Issue (2): some classes doesn’t seems to be known or available when initializing a class
E.g.

	*initClass {
		var test;
		test=Scale.minor;
		test.debug("Poc scale");
	}

throws a Build-time error because Scalereturns nil;

Issue (3): SynthDef.wrap using class methods as function returns an Execution-time error. Why ?
It can be replaced by a classvar holding a function. But because of (1) this function initialization has to be moved to the *initArgsmethod which leads to a less-readable code.

My question is: Am I doing this wrongly or are these limitations of SC ?


A full Class source code:

Poc {
	var current;
	// OK
	const anIntConst = 5;

	// Issue (1): BUILD TIME ERROR. This type of const is not accepted
	// const aColorConst = Color.new(0.8,0.7,0.6,1);
	// const aConst = [1,2,3];

	// Issue (1): BUILD TIME ERROR. This type of const is not accepted
	// classvar wrapperAsAFunc = {
	// 	|sig|
	// 	Out.ar(\out.kr(0),Pan2.ar(sig*EnvGate.new));
	// };
	classvar wrapperAsAFunc; // init moved to initClass


	*wrapperAsAMethod {
		|sig|
		sig=sig*LFNoise2.ar(2).linlin(-1,1,0,1);
		Out.ar(\out.kr(0),Pan2.ar(sig*EnvGate.new));
	}


	*new {^super.newCopyArgs();	}

	*initClass {
		var test;

		// w/a because defining this at the root of the class is not allowed.
		wrapperAsAFunc = {
			|sig|
			Out.ar(\out.kr(0),Pan2.ar(sig*EnvGate.new));
		};

		// ISSUE (2) - BUILD TIME ERROR - Scale class not known. Returns ERROR: Message 'at' not understood. RECEIVER: nil, ARGS: Symbol 'minor'
		// test=Scale.minor;
		test.debug("Poc scale")

	}

	playFromFunc {
		this.stop();
		if (current.isPlaying) { current.free; current=nil};
		SynthDef(\pocff,{
			var sig=SinOsc.ar(440)*0.2;
			SynthDef.wrap(wrapperAsAFunc, prependArgs: [sig]);
		}).add;

		current=Synth(\pocff).register;

	}

	playFromMethod {
		this.stop();
		SynthDef(\pocfm,{
			var sig=SinOsc.ar(440)*0.2;
			// Issue (3) - EXECUTION TIME ERROR
			SynthDef.wrap(Poc.wrapperAsAMethod, prependArgs: [sig]);
		}).add;

		current=Synth(\pocfm).register;

	}

	stop {
		if (current.isPlaying) { current.free; current=nil};
	}

}

And a try-out code:

x=Poc.new

x.playFromFunc; // OK
x.playFromMethod; // ERROR

x.stop

hi @lgvr123 - regarding issue2:
If you have a dependency between ClassA and ClassB (ie ClassA needs ClassB to have been initialized before it initializes itself) you can force SC to compile ClassB first like so

ClassA {
  *initClass {
    Class.initClassTree(ClassB);
    // ClassA's initialization here
  }
}

Class.initClassTree

1 Like

For issue 1, there are two choices:

  • arcanePsalms is correct about Class.initClassTree.
  • Or, if there aren’t any other classes depending on your class’s initialization, you can write:
Xyz {
	*initClass {
		StartUp.add {
			Scale.minor.postcs
		}
	}
}

The difference is that Class.initClassTree runs during the initClassTree process. StartUp functions run immediately after.

For issue 2 – you have:

	*wrapperAsAMethod {
		|sig|
		sig=sig*LFNoise2.ar(2).linlin(-1,1,0,1);
		Out.ar(\out.kr(0),Pan2.ar(sig*EnvGate.new));
	}

… which declares an argument sig.

Then:

SynthDef.wrap(Poc.wrapperAsAMethod, prependArgs: [sig]);

… where nothing is being passed to the argument’s method.

prependArgs is not an alternate syntax for Poc.wrapperAsAMethod(sig)… if you declare an argument sig, then something needs to be passed to it in the normal way.

So you could:

	playFromMethod {
		this.stop();
		SynthDef(\pocfm,{
			var sig=SinOsc.ar(440)*0.2;
			Poc.wrapperAsMethod(sig);
		}).add;
		
		current=Synth(\pocfm).register;
		
	}

Methods and functions are not interchangeable in SynthDef.wrap. (Note in your original example that you were not passing the method to wrap, but rather, passing the result of the method. It would be similar to SynthDef.wrap({ ... stuff ... }.value) and you’ll find that this doesn’t work either.)

hjh

1 Like

Thanks a lot.
What about the initialization of variables such as const c=[1,2,3]; ?
Is better approach than defining them in the initClass method ?