>= ... etc not working in Pfunc "If" statement

Hi,

I am trying to get a conditional statement working (\pan key) but I’m curiously not able to - this normally isn’t a problem. I can check for equivalence == but anything else throws an error. At first, I referenced the key \pos, and when I got the initial error I thought I’d try an “clean it up” by referencing the \numFrames key but to no luck. Is it a problem with me trying to accomplish this with Pseg? Any insight welcome, thank you

(
Pbind(
	\instrument,\sampler,
	\sndbuf,~sf,
	\dur, Pser([
		~durPatternA,
		Pseq([2.pow(-4)],16),
		Pseq([0.25],16),
	],inf), 
	\rate,2,
	\pan, Pfunc{
		|i|
		if(i [\numFrames] >=(829086.00).post)
		{rand2(1)}
		{0}
	},
	\pos, Pser([
		Pseg([~sf.numFrames*0.90, ~sf.numFrames*0.90],4,repeats:1),
		Pseg([Pbeta(~sf.numFrames*0.0,~sf.numFrames*0.5,0.4,0.6,1), Pbeta(~sf.numFrames*0.5,~sf.numFrames*1.0,0.4,0.6,1)],4,repeats:1),
	],inf),
	\numFrames, Pkey(\pos).trace,
).play;
)

http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_06g_Data_Sharing.html#Reading%20values%20from%20the%20current%20event

“Be aware that Pkey can only look backward to keys stated earlier in the Pbind definition. Pbind processes the keys in the order given. In the example, it would not work to put legato first and have it refer to degree coming later, because the degree value is not available yet.”

It’s the same principle for event[\key in a Pfunc – in fact, Pkey was introduced as a shorter alternative to Pfunc { |event| event[\key] }.

You can’t do numeric operations on a number that hasn’t been assigned yet.

hjh

1 Like

Hi, thanks for the reply - I’m smacking my head, I’ve rectified this problem myself in the past but it just flew by me this time. Appreciated!

There’s another thread somewhere on this forum, where I had experimented with an object that would allow dependent calculations to be written in any order.

Anyway, copy-paste the class definitions at the bottom into a .sc file in your extensions directory, and then – this is just taking your original Pbind and changing it to LazyPbind:

(
~sf = Buffer(s, 283821, 1, 1000);  // spoof
~durPatternA = Pwhite(1, 4, inf) * 0.25;

p = LazyPbind(
	\instrument, \sampler,
	\sndbuf, ~sf,
	\dur, Pser([
		~durPatternA,
		Pseq([2.pow(-4)], 16),
		Pseq([0.25], 16),
	], inf), 
	\rate, 2,
	\pan, Pfunc {
		|i|
		if(i [\numFrames] >=(829086.00))
		{rand2(1)}
		{0}
	},
	\pos, Pser([
		Pseg([~sf.numFrames*0.90, ~sf.numFrames*0.90], 4, repeats: 1),
		Pseg([Pbeta(~sf.numFrames*0.0, ~sf.numFrames*0.5, 0.4, 0.6, 1), Pbeta(~sf.numFrames*0.5, ~sf.numFrames*1.0, 0.4, 0.6, 1)], 4, repeats: 1),
	], inf),
	\numFrames, Pkey(\pos).trace,
);
)

q = p.asStream;
q.next(());
-> ( 'instrument': sampler, 'numFrames': 255438.9, 'dur': 0.75, 'sndbuf': Buffer(1000, 283821, 1, nil, nil), 
  'pan': 0, 'rate': 2, 'pos': 255438.9 )

What this does is: 1. When \pan tries to access i[\numFrames], it looks for \numFrames and resolves it. 2. To resolve \numFrames, it must resolve \pos. 3. \pos doesn’t refer to anything else in the event, so the pattern resolves normally and then it backtracks through the other keys to finish them.

It’s experimental – I don’t think it’s ready to pull into the class library. (Also, you might think, “Why isn’t this the default Pbind?” but it’s about three times slower, so it wouldn’t be appropriate for every use.)

a = Array.fill(50, { |i| [("param" ++ I).asSymbol, Pwhite(0, 99, inf)] }).flat;

(
p = Pbind(*a).asStream;
bench { 10000.do { p.next(Event.new) } };
)
time to run: 0.76619667600005 seconds.

(
p = LazyPbind(*a).asStream;
bench { 10000.do { p.next(Event.new) } };
)
time to run: 2.197149558 seconds.

hjh

// proof of concept - hjh 2020-09-11

// basically Thunk, possibly could fold this into Thunk
LazyResult : AbstractFunction {
	var func, value;
	var originalFunc;
	var <>method;

	*new { |func, method(\value)|
		^super.newCopyArgs(func, nil, func, method)
	}

	value { |... args|
		^if(this.isResolved) {
			value
		} {
			value = func.performList(method, args);
			func = nil;
			value
		}
	}

	isResolved { ^func.isNil }

	reset { func = originalFunc; value = nil }
}

LambdaEnvir {
	var envir;
	var resolvingKeys;

	*new { |env, method|
		^super.new.init(env, method);
	}

	init { |env, method|
		envir = Environment.new;
		env.keysValuesDo { |key, value|
			envir.put(key, LazyResult(value, method));
		};
	}

	at { |key|
		var entity = envir.at(key);
		if(entity.isResolved) {
			// should be an already-calculated value,
			// but we need to return it below
			entity = entity.value;
		} {
			if(resolvingKeys.isNil) {
				resolvingKeys = IdentitySet.new;
			};
			if(resolvingKeys.includes(key)) {
				Error("LambdaEnvir: Circular reference involving '%'".format(key)).throw;
			};
			resolvingKeys.add(key);
			protect {
				this.use {
					entity = entity.value(this);
					// "LambdaEnvir resolved '%' to %\n".postf(key, entity);
				};
			} {
				resolvingKeys.remove(key);
			};
		};
		^entity
	}

	put { |key, value| envir.put(key, value) }

	use { |func|
		var saveEnvir;
		var result;
		if(currentEnvironment === this) {
			^func.value
		} {
			protect {
				saveEnvir = currentEnvironment;
				currentEnvironment = this;
				result = func.value;
			} {
				currentEnvironment = saveEnvir;
			};
			^result
		};
	}

	keysValuesDo { |func|
		envir.keysValuesDo(func)
	}

	reset {
		this.keysValuesDo { |key, value| value.reset }
	}

	parent { ^envir.parent }
	proto { ^envir.proto }
	parent_ { |parent| envir.parent_(parent) }
	proto_ { |proto| envir.proto_(proto) }
}

LazyPbind : Pbind {
	embedInStream { |inevent|
		var proto = Event.new;
		var lambdaEnvir;
		var event;

		patternpairs.pairsDo { |key, value|
			proto.put(key, value.asStream)
		};

		loop {
			if(inevent.isNil) { ^nil.yield };
			lambdaEnvir = LambdaEnvir(proto, method: \next)
			.parent_(inevent.parent).proto_(inevent.proto);
			event = inevent.copy;
			lambdaEnvir.keysValuesDo { |key|
				var value = lambdaEnvir[key];
				if(value.isNil) { ^inevent };
				if(key.isSequenceableCollection) {
					if(key.size > value.size) {
						("the pattern is not providing enough values to assign to the key set:" + key).warn;
						^inevent
					};
					key.do { |key1, i|
						event.put(key1, value[i]);
					};
				} {
					event.put(key, value);
				};
			};
			inevent = event.yield;
		}
	}
}

+ Object {
	isResolved { ^true }
}