Question About Pseries Argument Behavior

Hi all,

I’ve recently noticed that the arguments of Pseries behave differently depending on how they are used. Specifically, it works fine when I pass a pattern into the step: argument, but I get an error when I try to do the same with the length: argument.

This works:

(
a = Pbind( 
	\midinote, Pseries(
		start: 50,
		step: Pbrown(-1.0, 3.0, 0.1, inf), 
		length: 5		
	).loop,
	\dur, 0.1
);
p = a.play;
)
p.stop;

This gives an error (“Non Boolean in test”):

(
a = Pbind( 
	\midinote, Pseries(
		start: 50,
		step: Pbrown(-1.0, 3.0, 0.1, inf), 
		length: Pseq([5, 10], inf)		
	).loop,
	\dur, 0.1
);
p = a.play;
)
p.stop;

Why does this happen? Is length: not allowed to take a pattern input, or is there a different way to achieve variable lengths in a Pseries?

Thanks in advance for any insights!

Yes:

Pseries(
	start: 50,
	step: Pbrown(-1.0, 3.0, 0.1, inf),
	length: Pseq([5, 10], inf).asStream
)

The reason why is subtle.

It makes sense to allow a different step per output value – so a pattern appearing here can be automatically stream-ified. Every time the Pseries begins, the step pattern will begin from the beginning, and this is OK because there is one step value per output value.

It does not make sense to change the length in the middle of a Pseries. (What if you’ve already output 10 values, and the length changes to 6?) So, upon entering a Pseries, one length value is pulled from its stream. Now the catch is: If you supply a pattern here, and expect .asStream to apply to it automatically, then every time you enter that Pseries, a new stream will be created, and only one value will be pulled from it, meaning that only the first value from the pattern will ever apply.

In your pattern, you would get series of length 5 only – never 10.

So you don’t actually want patterns to be valid for length. It seems like it should be valid, but it will impose an undesired limitation. In a way, it’s good that it throws an error here. (The error isn’t informative…)

The solution is to call asStream yourself on the length pattern, as in my edit above. Or, if it’s an expression rather than a pattern, a function may be used: length: { rrand(3, 7) }.

hjh

Thank you! As I’ve understood it, in Pseries, the step pattern is evaluated for each event, while the length pattern is evaluated only once when the stream starts. Because Pseries resets its input patterns every time a new stream is created, the length pattern ends up returning the same value repeatedly, which doesn’t make sense indeed, therefore sclang becomes aware and yells at me with an error message that happens to be kinda confusing. Just trying out “.asStream” on value patterns led me to very interesting variations to existing patterns i didn’t notice before. I’ll pay more attention to the consequences of passing a routine into a keyword vs passing a pattern. Best, Sinan

There are a couple steps I left out.

Patterns treat each input in one of two ways:

  • “Initial value” inputs get evaluated once by .value. For Pseries, this is both start and length. .value works on ordinary values (returns itself), or functions (returns the function’s value) or streams (returns the next value).
  • Or, “continuously variable” inputs get converted into a local stream by .asStream, and then this is evaluated for every output value. This works on ordinary values, patterns (new stream) or streams (use the given stream itself). In Pseries, this is step.

If you give a pattern for an initial-value input, then it’s .value-d, which returns the pattern itself. Then it will try to do something like count < len. Binary operations on patterns produce a new pattern – which is not a Boolean, so it can’t be used for loop control = error.

It isn’t a matter of awareness; it’s a matter of different handling of those two types of input.

It’s definitely a challenge when learning patterns, that the two types of input look the same but mean different things (could even be called a weakness in the object interfaces). Unfortunately one can only learn which ones are which. Documentation about this specific point isn’t systematic, I’m afraid.

hjh

1 Like

Reading your explanation alongside the Pseries class definition really clarifies the issue, thanks! The Patterns library can feel like a black box at times, but checking the class definition to see whether inputs are .value-d or .asStream-ed will definitely help.
Best, s